passport_edit_identity_box.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "passport/passport_edit_identity_box.h"
  8. #include "passport/passport_panel_controller.h"
  9. #include "ui/widgets/fields/input_field.h"
  10. #include "ui/widgets/buttons.h"
  11. #include "ui/text_options.h"
  12. #include "lang/lang_keys.h"
  13. #include "core/file_utilities.h"
  14. #include "styles/style_widgets.h"
  15. #include "styles/style_boxes.h"
  16. #include "styles/style_passport.h"
  17. namespace Passport {
  18. class ScanButton : public Ui::RippleButton {
  19. public:
  20. ScanButton(
  21. QWidget *parent,
  22. const QString &title,
  23. const QString &description);
  24. void setImage(const QImage &image);
  25. void setDescription(const QString &description);
  26. rpl::producer<> deleteClicks() const {
  27. return _delete->clicks();
  28. }
  29. protected:
  30. int resizeGetHeight(int newWidth) override;
  31. void paintEvent(QPaintEvent *e) override;
  32. private:
  33. int countAvailableWidth() const;
  34. int countAvailableWidth(int newWidth) const;
  35. Text _title;
  36. Text _description;
  37. int _titleHeight = 0;
  38. int _descriptionHeight = 0;
  39. QImage _image;
  40. object_ptr<Ui::IconButton> _delete = { nullptr };
  41. };
  42. ScanButton::ScanButton(
  43. QWidget *parent,
  44. const QString &title,
  45. const QString &description)
  46. : RippleButton(parent, st::passportRowRipple)
  47. , _title(
  48. st::semiboldTextStyle,
  49. title,
  50. Ui::NameTextOptions())
  51. , _description(
  52. st::defaultTextStyle,
  53. description,
  54. Ui::NameTextOptions())
  55. , _delete(this, st::passportScanDelete) {
  56. }
  57. void ScanButton::setImage(const QImage &image) {
  58. _image = image;
  59. update();
  60. }
  61. void ScanButton::setDescription(const QString &description) {
  62. _description.setText(
  63. st::defaultTextStyle,
  64. description,
  65. Ui::NameTextOptions());
  66. update();
  67. }
  68. int ScanButton::resizeGetHeight(int newWidth) {
  69. const auto availableWidth = countAvailableWidth(newWidth);
  70. _titleHeight = st::semiboldFont->height;
  71. _descriptionHeight = st::normalFont->height;
  72. const auto result = st::passportRowPadding.top()
  73. + _titleHeight
  74. + st::passportRowSkip
  75. + _descriptionHeight
  76. + st::passportRowPadding.bottom();
  77. const auto right = st::passportRowPadding.right();
  78. _delete->moveToRight(
  79. right,
  80. (result - _delete->height()) / 2,
  81. newWidth);
  82. return result;
  83. }
  84. int ScanButton::countAvailableWidth(int newWidth) const {
  85. return newWidth
  86. - st::passportRowPadding.left()
  87. - st::passportRowPadding.right()
  88. - _delete->width();
  89. }
  90. int ScanButton::countAvailableWidth() const {
  91. return countAvailableWidth(width());
  92. }
  93. void ScanButton::paintEvent(QPaintEvent *e) {
  94. Painter p(this);
  95. const auto ms = getms();
  96. paintRipple(p, 0, 0, ms);
  97. auto left = st::passportRowPadding.left();
  98. auto availableWidth = countAvailableWidth();
  99. auto top = st::passportRowPadding.top();
  100. const auto size = height() - top - st::passportRowPadding.bottom();
  101. if (_image.isNull()) {
  102. p.fillRect(left, top, size, size, Qt::black);
  103. } else {
  104. PainterHighQualityEnabler hq(p);
  105. if (_image.width() > _image.height()) {
  106. auto newheight = size * _image.height() / _image.width();
  107. p.drawImage(QRect(left, top + (size - newheight) / 2, size, newheight), _image);
  108. } else {
  109. auto newwidth = size * _image.width() / _image.height();
  110. p.drawImage(QRect(left + (size - newwidth) / 2, top, newwidth, size), _image);
  111. }
  112. }
  113. left += size + st::passportRowPadding.left();
  114. availableWidth -= size + st::passportRowPadding.left();
  115. _title.drawLeftElided(p, left, top, availableWidth, width());
  116. top += _titleHeight + st::passportRowSkip;
  117. _description.drawLeftElided(p, left, top, availableWidth, width());
  118. top += _descriptionHeight + st::passportRowPadding.bottom();
  119. }
  120. IdentityBox::IdentityBox(
  121. QWidget*,
  122. not_null<PanelController*> controller,
  123. int valueIndex,
  124. const IdentityData &data,
  125. std::vector<ScanInfo> &&files)
  126. : _controller(controller)
  127. , _valueIndex(valueIndex)
  128. , _files(std::move(files))
  129. , _uploadScan(this, "Upload scans") // #TODO langs
  130. , _name(
  131. this,
  132. st::defaultInputField,
  133. langFactory(lng_signup_firstname),
  134. data.name)
  135. , _surname(
  136. this,
  137. st::defaultInputField,
  138. langFactory(lng_signup_lastname),
  139. data.surname) {
  140. }
  141. void IdentityBox::prepare() {
  142. setTitle(langFactory(lng_passport_identity_title));
  143. auto index = 0;
  144. for (const auto &scan : _files) {
  145. _scans.push_back(object_ptr<ScanButton>(
  146. this,
  147. QString("Scan %1").arg(++index), // #TODO langs
  148. scan.status));
  149. _scans.back()->setImage(scan.thumb);
  150. _scans.back()->resizeToWidth(st::boxWideWidth);
  151. _scans.back()->deleteClicks(
  152. ) | rpl::start_with_next([=] {
  153. _controller->deleteScan(_valueIndex, index - 1);
  154. }, lifetime());
  155. }
  156. addButton(langFactory(lng_settings_save), [=] {
  157. save();
  158. });
  159. addButton(langFactory(lng_cancel), [=] {
  160. closeBox();
  161. });
  162. _controller->scanUpdated(
  163. ) | rpl::start_with_next([=](ScanInfo &&info) {
  164. updateScan(std::move(info));
  165. }, lifetime());
  166. _uploadScan->addClickHandler([=] {
  167. chooseScan();
  168. });
  169. setDimensions(st::boxWideWidth, countHeight());
  170. }
  171. int IdentityBox::countHeight() const {
  172. auto height = st::contactPadding.top();
  173. for (const auto &scan : _scans) {
  174. height += scan->height();
  175. }
  176. height += st::contactPadding.top()
  177. + _uploadScan->height()
  178. + st::contactSkip
  179. + _name->height()
  180. + st::contactSkip
  181. + _surname->height()
  182. + st::contactPadding.bottom()
  183. + st::boxPadding.bottom();
  184. return height;
  185. }
  186. void IdentityBox::updateScan(ScanInfo &&info) {
  187. const auto i = ranges::find(_files, info.key, [](const ScanInfo &file) {
  188. return file.key;
  189. });
  190. if (i != _files.end()) {
  191. *i = info;
  192. _scans[i - _files.begin()]->setDescription(i->status);
  193. _scans[i - _files.begin()]->setImage(i->thumb);
  194. } else {
  195. _files.push_back(std::move(info));
  196. _scans.push_back(object_ptr<ScanButton>(
  197. this,
  198. QString("Scan %1").arg(_files.size()),
  199. _files.back().status));
  200. _scans.back()->setImage(_files.back().thumb);
  201. _scans.back()->resizeToWidth(st::boxWideWidth);
  202. _scans.back()->show();
  203. updateControlsPosition();
  204. setDimensions(st::boxWideWidth, countHeight());
  205. }
  206. update();
  207. }
  208. void IdentityBox::setInnerFocus() {
  209. _name->setFocusFast();
  210. }
  211. void IdentityBox::resizeEvent(QResizeEvent *e) {
  212. BoxContent::resizeEvent(e);
  213. _name->resize((width()
  214. - st::boxPadding.left()
  215. - st::boxPadding.right()),
  216. _name->height());
  217. _surname->resize(_name->width(), _surname->height());
  218. updateControlsPosition();
  219. }
  220. void IdentityBox::updateControlsPosition() {
  221. auto top = st::contactPadding.top();
  222. for (const auto &scan : _scans) {
  223. scan->moveToLeft(0, top);
  224. top += scan->height();
  225. }
  226. top += st::contactPadding.top();
  227. _uploadScan->moveToLeft(st::boxPadding.left(), top);
  228. top += _uploadScan->height() + st::contactSkip;
  229. _name->moveToLeft(st::boxPadding.left(), top);
  230. top += _name->height() + st::contactSkip;
  231. _surname->moveToLeft(st::boxPadding.left(), top);
  232. }
  233. void IdentityBox::chooseScan() {
  234. const auto filter = FileDialog::AllFilesFilter()
  235. + u";;Image files (*"_q
  236. + cImgExtensions().join(u" *"_q)
  237. + u")"_q;
  238. const auto callback = [=](FileDialog::OpenResult &&result) {
  239. if (result.paths.size() == 1) {
  240. encryptScan(result.paths.front());
  241. } else if (!result.remoteContent.isEmpty()) {
  242. encryptScanContent(std::move(result.remoteContent));
  243. }
  244. };
  245. FileDialog::GetOpenPath(
  246. "Choose scan image",
  247. filter,
  248. base::lambda_guarded(this, callback));
  249. }
  250. void IdentityBox::encryptScan(const QString &path) {
  251. encryptScanContent([&] {
  252. QFile f(path);
  253. if (!f.open(QIODevice::ReadOnly)) {
  254. return QByteArray();
  255. }
  256. return f.readAll();
  257. }());
  258. }
  259. void IdentityBox::encryptScanContent(QByteArray &&content) {
  260. _controller->uploadScan(_valueIndex, std::move(content));
  261. }
  262. void IdentityBox::save() {
  263. auto data = IdentityData();
  264. data.name = _name->getLastText();
  265. data.surname = _surname->getLastText();
  266. _controller->saveValueIdentity(_valueIndex, data);
  267. }
  268. } // namespace Passport