tdesktop/Telegram/SourceFiles/passport/passport_edit_identity_box.cpp

308 lines
7.7 KiB
C++

/*
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 "passport/passport_edit_identity_box.h"
#include "passport/passport_panel_controller.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/buttons.h"
#include "ui/text_options.h"
#include "lang/lang_keys.h"
#include "core/file_utilities.h"
#include "styles/style_widgets.h"
#include "styles/style_boxes.h"
#include "styles/style_passport.h"
namespace Passport {
class ScanButton : public Ui::RippleButton {
public:
ScanButton(
QWidget *parent,
const QString &title,
const QString &description);
void setImage(const QImage &image);
void setDescription(const QString &description);
rpl::producer<> deleteClicks() const {
return _delete->clicks();
}
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
int countAvailableWidth() const;
int countAvailableWidth(int newWidth) const;
Text _title;
Text _description;
int _titleHeight = 0;
int _descriptionHeight = 0;
QImage _image;
object_ptr<Ui::IconButton> _delete = { nullptr };
};
ScanButton::ScanButton(
QWidget *parent,
const QString &title,
const QString &description)
: RippleButton(parent, st::passportRowRipple)
, _title(
st::semiboldTextStyle,
title,
Ui::NameTextOptions())
, _description(
st::defaultTextStyle,
description,
Ui::NameTextOptions())
, _delete(this, st::passportScanDelete) {
}
void ScanButton::setImage(const QImage &image) {
_image = image;
update();
}
void ScanButton::setDescription(const QString &description) {
_description.setText(
st::defaultTextStyle,
description,
Ui::NameTextOptions());
update();
}
int ScanButton::resizeGetHeight(int newWidth) {
const auto availableWidth = countAvailableWidth(newWidth);
_titleHeight = st::semiboldFont->height;
_descriptionHeight = st::normalFont->height;
const auto result = st::passportRowPadding.top()
+ _titleHeight
+ st::passportRowSkip
+ _descriptionHeight
+ st::passportRowPadding.bottom();
const auto right = st::passportRowPadding.right();
_delete->moveToRight(
right,
(result - _delete->height()) / 2,
newWidth);
return result;
}
int ScanButton::countAvailableWidth(int newWidth) const {
return newWidth
- st::passportRowPadding.left()
- st::passportRowPadding.right()
- _delete->width();
}
int ScanButton::countAvailableWidth() const {
return countAvailableWidth(width());
}
void ScanButton::paintEvent(QPaintEvent *e) {
Painter p(this);
const auto ms = getms();
paintRipple(p, 0, 0, ms);
auto left = st::passportRowPadding.left();
auto availableWidth = countAvailableWidth();
auto top = st::passportRowPadding.top();
const auto size = height() - top - st::passportRowPadding.bottom();
if (_image.isNull()) {
p.fillRect(left, top, size, size, Qt::black);
} else {
PainterHighQualityEnabler hq(p);
if (_image.width() > _image.height()) {
auto newheight = size * _image.height() / _image.width();
p.drawImage(QRect(left, top + (size - newheight) / 2, size, newheight), _image);
} else {
auto newwidth = size * _image.width() / _image.height();
p.drawImage(QRect(left + (size - newwidth) / 2, top, newwidth, size), _image);
}
}
left += size + st::passportRowPadding.left();
availableWidth -= size + st::passportRowPadding.left();
_title.drawLeftElided(p, left, top, availableWidth, width());
top += _titleHeight + st::passportRowSkip;
_description.drawLeftElided(p, left, top, availableWidth, width());
top += _descriptionHeight + st::passportRowPadding.bottom();
}
IdentityBox::IdentityBox(
QWidget*,
not_null<PanelController*> controller,
int valueIndex,
const IdentityData &data,
std::vector<ScanInfo> &&files)
: _controller(controller)
, _valueIndex(valueIndex)
, _files(std::move(files))
, _uploadScan(this, "Upload scans") // #TODO langs
, _name(
this,
st::defaultInputField,
langFactory(lng_signup_firstname),
data.name)
, _surname(
this,
st::defaultInputField,
langFactory(lng_signup_lastname),
data.surname) {
}
void IdentityBox::prepare() {
setTitle(langFactory(lng_passport_identity_title));
auto index = 0;
for (const auto &scan : _files) {
_scans.push_back(object_ptr<ScanButton>(
this,
QString("Scan %1").arg(++index), // #TODO langs
scan.status));
_scans.back()->setImage(scan.thumb);
_scans.back()->resizeToWidth(st::boxWideWidth);
_scans.back()->deleteClicks(
) | rpl::start_with_next([=] {
_controller->deleteScan(_valueIndex, index - 1);
}, lifetime());
}
addButton(langFactory(lng_settings_save), [=] {
save();
});
addButton(langFactory(lng_cancel), [=] {
closeBox();
});
_controller->scanUpdated(
) | rpl::start_with_next([=](ScanInfo &&info) {
updateScan(std::move(info));
}, lifetime());
_uploadScan->addClickHandler([=] {
chooseScan();
});
setDimensions(st::boxWideWidth, countHeight());
}
int IdentityBox::countHeight() const {
auto height = st::contactPadding.top();
for (const auto &scan : _scans) {
height += scan->height();
}
height += st::contactPadding.top()
+ _uploadScan->height()
+ st::contactSkip
+ _name->height()
+ st::contactSkip
+ _surname->height()
+ st::contactPadding.bottom()
+ st::boxPadding.bottom();
return height;
}
void IdentityBox::updateScan(ScanInfo &&info) {
const auto i = ranges::find(_files, info.key, [](const ScanInfo &file) {
return file.key;
});
if (i != _files.end()) {
*i = info;
_scans[i - _files.begin()]->setDescription(i->status);
_scans[i - _files.begin()]->setImage(i->thumb);
} else {
_files.push_back(std::move(info));
_scans.push_back(object_ptr<ScanButton>(
this,
QString("Scan %1").arg(_files.size()),
_files.back().status));
_scans.back()->setImage(_files.back().thumb);
_scans.back()->resizeToWidth(st::boxWideWidth);
_scans.back()->show();
updateControlsPosition();
setDimensions(st::boxWideWidth, countHeight());
}
update();
}
void IdentityBox::setInnerFocus() {
_name->setFocusFast();
}
void IdentityBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_name->resize((width()
- st::boxPadding.left()
- st::boxPadding.right()),
_name->height());
_surname->resize(_name->width(), _surname->height());
updateControlsPosition();
}
void IdentityBox::updateControlsPosition() {
auto top = st::contactPadding.top();
for (const auto &scan : _scans) {
scan->moveToLeft(0, top);
top += scan->height();
}
top += st::contactPadding.top();
_uploadScan->moveToLeft(st::boxPadding.left(), top);
top += _uploadScan->height() + st::contactSkip;
_name->moveToLeft(st::boxPadding.left(), top);
top += _name->height() + st::contactSkip;
_surname->moveToLeft(st::boxPadding.left(), top);
}
void IdentityBox::chooseScan() {
const auto filter = FileDialog::AllFilesFilter()
+ qsl(";;Image files (*")
+ cImgExtensions().join(qsl(" *"))
+ qsl(")");
const auto callback = [=](FileDialog::OpenResult &&result) {
if (result.paths.size() == 1) {
encryptScan(result.paths.front());
} else if (!result.remoteContent.isEmpty()) {
encryptScanContent(std::move(result.remoteContent));
}
};
FileDialog::GetOpenPath(
"Choose scan image",
filter,
base::lambda_guarded(this, callback));
}
void IdentityBox::encryptScan(const QString &path) {
encryptScanContent([&] {
QFile f(path);
if (!f.open(QIODevice::ReadOnly)) {
return QByteArray();
}
return f.readAll();
}());
}
void IdentityBox::encryptScanContent(QByteArray &&content) {
_controller->uploadScan(_valueIndex, std::move(content));
}
void IdentityBox::save() {
auto data = IdentityData();
data.name = _name->getLastText();
data.surname = _surname->getLastText();
_controller->saveValueIdentity(_valueIndex, data);
}
} // namespace Passport