New design of the passport in a separate window.

This commit is contained in:
John Preston 2018-03-31 05:45:40 +04:00
parent a2dabfde56
commit 5cfead762d
36 changed files with 2112 additions and 555 deletions

View File

@ -1503,26 +1503,38 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_terms_signup_sorry" = "We're very sorry, but this means you can't sign up for Telegram.\n\nUnlike others, we don't use your data for ad targeting or other commercial purposes. Telegram only stores the information it needs to function as a feature-rich cloud service. You can adjust how we use your data in Privacy & Security settings.\n\nBut if you're generally not OK with Telegram's modest needs, it won't be possible for us to provide this service.";
"lng_passport_title" = "Telegram passport";
"lng_passport_request" = "{domain} requests access to your personal data\nto sign you up for their services";
"lng_passport_password_request" = "Please provide the password for\naccessing your personal data.";
"lng_passport_password_placeholder" = "Enter password";
"lng_passport_request1" = "{bot} requests access to your personal data";
"lng_passport_request2" = "to sign you up for their services";
"lng_passport_password_placeholder" = "Your password";
"lng_passport_next" = "Next";
"lng_passport_password_wrong" = "Wrong password";
"lng_passport_header" = "Requested information";
"lng_passport_identity_title" = "Identity document";
"lng_passport_identity_description" = "Upload scan of your passport or other ID";
"lng_passport_identity_description" = "Upload a scan of your passport or other ID";
"lng_passport_identity_passport" = "Passport";
"lng_passport_identity_card" = "Identity card";
"lng_passport_identity_license" = "Driver's license";
"lng_passport_address_title" = "Residential address";
"lng_passport_address_description" = "Upload proof of your address";
"lng_passport_address_description" = "Upload a proof of your address";
"lng_passport_phone_title" = "Phone number";
"lng_passport_email_title" = "E-mail";
"lng_passport_email_description" = "Specify your e-mail address";
"lng_passport_phone_description" = "Enter your phone number";
"lng_passport_email_title" = "Email";
"lng_passport_email_description" = "Enter your email address";
"lng_passport_accept_allow" = "You accept {policy} and allow their {bot} to send messages to you.";
"lng_passport_accept" = "You accept {policy}.";
"lng_passport_policy" = "{domain} privacy policy";
"lng_passport_policy" = "{bot} privacy policy";
"lng_passport_authorize" = "Authorize";
"lng_passport_form_error" = "Could not get authorization form.";
"lng_passport_save_value" = "Done";
"lng_passport_upload_header" = "Scans";
"lng_passport_scan_index" = "Scan {index}";
"lng_passport_upload_scans" = "Upload scans";
"lng_passport_upload_more" = "Upload additional scans";
"lng_passport_personal_details" = "Personal details";
"lng_passport_choose_image" = "Choose scan image";
"lng_passport_delete_scan_undo" = "Undo";
"lng_passport_scan_uploaded" = "Uploaded on {date}";
"lng_passport_first_name" = "First name";
"lng_passport_last_name" = "Last name";
// Wnd specific

View File

@ -281,7 +281,7 @@ messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDis
messageActionScreenshotTaken#4792929b = MessageAction;
messageActionCustomAction#fae69f56 message:string = MessageAction;
messageActionBotAllowed#abe9affe domain:string = MessageAction;
messageActionSecureValuesSentMe#9bc8ec4 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted payload:bytes = MessageAction;
messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
@ -982,15 +982,15 @@ secureValueTypeAddress#cbe31e26 = SecureValueType;
secureValueTypePhone#b320aadb = SecureValueType;
secureValueTypeEmail#8e3ca7ee = SecureValueType;
secureValueIdentity#4838ff84 flags:# data:SecureData files:Vector<SecureFile> hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
secureValueAddress#2b9f6bef flags:# data:SecureData files:Vector<SecureFile> hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
secureValuePhone#a1ca84fe flags:# phone:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
secureValueEmail#c4db6579 flags:# email:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
secureValueIdentity#15fe72a2 flags:# data:SecureData files:Vector<SecureFile> verified:flags.0?SecureValueVerified = SecureValue;
secureValueAddress#88109b79 flags:# data:SecureData files:Vector<SecureFile> verified:flags.0?SecureValueVerified = SecureValue;
secureValuePhone#e39470bf flags:# phone:string verified:flags.0?SecureValueVerified = SecureValue;
secureValueEmail#35d804cd flags:# email:string verified:flags.0?SecureValueVerified = SecureValue;
inputSecureValueIdentity#df93404 data:SecureData files:Vector<InputSecureFile> hash:bytes = InputSecureValue;
inputSecureValueAddress#5589502 data:SecureData files:Vector<InputSecureFile> hash:bytes = InputSecureValue;
inputSecureValuePhone#141e00b8 phone:string hash:bytes = InputSecureValue;
inputSecureValueEmail#2dc15b9a email:string hash:bytes = InputSecureValue;
inputSecureValueIdentity#94fa65b data:SecureData files:Vector<InputSecureFile> = InputSecureValue;
inputSecureValueAddress#96689355 data:SecureData files:Vector<InputSecureFile> = InputSecureValue;
inputSecureValuePhone#9d623d96 phone:string = InputSecureValue;
inputSecureValueEmail#9e885359 email:string = InputSecureValue;
secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
@ -1061,7 +1061,7 @@ account.getSecureValue#d97e77cb user_id:InputUser types:Vector<SecureValueType>
account.saveSecureValue#78969d0b value:InputSecureValue secure_secret_id:long = SecureValueSaved;
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
account.acceptAuthorization#8d5e02e6 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted payload:bytes = Bool;
account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;

View File

@ -438,11 +438,13 @@ void AbstractBox::keyPressEvent(QKeyEvent *e) {
}
}
BoxContentDivider::BoxContentDivider(QWidget *parent) : RpWidget(parent) {
BoxContentDivider::BoxContentDivider(QWidget *parent)
: BoxContentDivider(parent, st::rightsDividerHeight) {
}
int BoxContentDivider::resizeGetHeight(int newWidth) {
return st::rightsDividerHeight;
BoxContentDivider::BoxContentDivider(QWidget *parent, int height)
: RpWidget(parent) {
resize(width(), height);
}
void BoxContentDivider::paintEvent(QPaintEvent *e) {

View File

@ -298,9 +298,9 @@ private:
class BoxContentDivider : public Ui::RpWidget {
public:
BoxContentDivider(QWidget *parent);
BoxContentDivider(QWidget *parent, int height);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
};

View File

@ -20,6 +20,7 @@ CallSignalBars {
callWidth: 300px;
callHeight: 470px;
callRadius: 6px;
callShadow: Shadow {
left: icon {{ "call_shadow_left", windowShadowFg }};
topLeft: icon {{ "call_shadow_top_left", windowShadowFg }};

View File

@ -600,7 +600,7 @@ void Panel::createBottomImage() {
p.setBrush(st::callBg);
p.setPen(Qt::NoPen);
PainterHighQualityEnabler hq(p);
p.drawRoundedRect(myrtlrect(_padding.left(), -st::historyMessageRadius, st::callWidth, bottomHeight - _padding.bottom() + st::historyMessageRadius), st::historyMessageRadius, st::historyMessageRadius);
p.drawRoundedRect(myrtlrect(_padding.left(), -st::callRadius, st::callWidth, bottomHeight - _padding.bottom() + st::callRadius), st::callRadius, st::callRadius);
}
_bottomCache = App::pixmapFromImageInPlace(std::move(image));
}
@ -620,7 +620,7 @@ void Panel::createDefaultCacheImage() {
p.setBrush(st::callBg);
p.setPen(Qt::NoPen);
PainterHighQualityEnabler hq(p);
p.drawRoundedRect(myrtlrect(inner), st::historyMessageRadius, st::historyMessageRadius);
p.drawRoundedRect(myrtlrect(inner), st::callRadius, st::callRadius);
}
_cache = App::pixmapFromImageInPlace(std::move(cache));
}

View File

@ -9,31 +9,177 @@ using "basic.style";
using "ui/widgets/widgets.style";
using "boxes/boxes.style";
using "intro/intro.style";
using "info/info.style";
using "chat_helpers/chat_helpers.style";
passportPasswordPadding: margins(20px, 30px, 20px, 40px);
passportPasswordForgotSkip: 5px;
passportPasswordAboutSkip: 15px;
passportPasswordLabel: FlatLabel(boxLabel) {
minWidth: 275px;
align: align(top);
}
passportPasswordLabelBold: FlatLabel(passportPasswordLabel) {
style: TextStyle(boxLabelStyle) {
font: font(boxFontSize semibold);
linkFont: font(boxFontSize semibold);
linkFontOver: font(boxFontSize semibold underline);
}
}
passportPasswordHintLabel: passportPasswordLabel;
passportErrorLabel: FlatLabel(passportPasswordLabel) {
textFg: boxTextFgError;
}
passportRowPadding: margins(20px, 10px, 20px, 10px);
passportRowSkip: 5px;
passportPanelWidth: 392px;
passportPanelHeight: 600px;
passportPanelBorderCacheSize: 60px;
passportPanelTitleHeight: 62px;
passportPanelClose: IconButton(boxTitleClose) {
width: 60px;
height: 60px;
rippleAreaPosition: point(8px, 8px);
rippleAreaSize: 44px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
passportPanelTitleFont: font(18px semibold);
passportPanelTitle: FlatLabel(defaultFlatLabel) {
textFg: boxTitleFg;
maxHeight: 26px;
style: TextStyle(defaultTextStyle) {
font: passportPanelTitleFont;
linkFont: passportPanelTitleFont;
linkFontOver: font(18px semibold underline);
}
}
passportPanelTitleTop: 18px;
passportPanelTitleLeft: 22px;
passportPanelTitleSkip: 0px;
passportPanelBack: IconButton(passportPanelClose) {
icon: infoTopBarBackIcon;
iconOver: infoTopBarBackIconOver;
}
passportPasswordFieldBottom: 306px;
passportPasswordFieldSkip: 29px;
passportPasswordHintSkip: 10px;
passportPasswordUserpicSkip: 14px;
passportPasswordUserpic: UserpicButton(defaultUserpicButton) {
size: size(80px, 80px);
photoSize: 80px;
photoPosition: point(0px, 0px);
}
passportPasswordSubmit: RoundButton(defaultActiveButton) {
width: 200px;
height: 44px;
textTop: 12px;
font: font(semibold 15px);
}
passportPasswordSubmitBottom: 72px;
passportPasswordForgotBottom: 36px;
passportPanelScroll: ScrollArea(defaultScrollArea) {
deltat: 6px;
deltab: 6px;
topsh: 0px;
bottomsh: 0px;
}
passportPanelAuthorize: RoundButton(passportPasswordSubmit) {
width: 0px;
height: 49px;
padding: margins(0px, -3px, 0px, 0px);
textTop: 16px;
}
passportPanelSaveValue: RoundButton(passportPanelAuthorize) {
textFg: windowActiveTextFg;
textFgOver: windowActiveTextFg;
textBg: windowBg;
textBgOver: windowBgOver;
ripple: defaultRippleAnimation;
}
passportFormAbout1Padding: margins(10px, 4px, 10px, 0px);
passportFormAbout2Padding: margins(10px, 0px, 10px, 22px);
passportFormHeader: FlatLabel(boxLabel) {
textFg: windowActiveTextFg;
style: semiboldTextStyle;
}
passportFormHeaderPadding: margins(22px, 20px, 22px, 9px);
passportFormUserpic: UserpicButton(passportPasswordUserpic) {
size: size(60px, 60px);
photoSize: 60px;
}
passportFormUserpicPadding: margins(0px, 5px, 0px, 10px);
passportFormDividerHeight: 13px;
passportFormPolicy: FlatLabel(defaultFlatLabel) {
minWidth: 285px;
align: align(topleft);
textFg: windowSubTextFg;
style: TextStyle(defaultTextStyle) {
linkFont: font(fsize semibold underline);
linkFontOver: font(fsize semibold underline);
}
palette: TextPalette(defaultTextPalette) {
linkFg: windowSubTextFg;
}
}
passportFormPolicyPadding: margins(22px, 7px, 22px, 28px);
passportRowPadding: margins(22px, 8px, 25px, 8px);
passportRowSkip: 2px;
passportRowRipple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
passportRowCheckbox: IconButton(defaultIconButton) {
width: 38px;
height: 38px;
iconPosition: point(10px, 13px);
icon: icon {{ "send_control_save", menuIconFg }};
iconOver: icon {{ "send_control_save", menuIconFgOver }};
rippleAreaPosition: point(0px, 0px);
rippleAreaSize: 38px;
ripple: passportRowRipple;
passportRowReadyIcon: icon {{ "send_control_save", windowActiveTextFg }};
passportRowEmptyIcon: icon {{ "title_back-flip_horizontal", menuIconFgOver }};
passportRowTitleFg: windowFg;
passportRowDescriptionFg: windowSubTextFg;
passportScansHeaderPadding: margins(22px, 10px, 22px, 10px);
passportUploadButton: InfoProfileButton {
textFg: windowActiveTextFg;
textFgOver: windowActiveTextFg;
textBg: windowBg;
textBgOver: windowBgOver;
font: semiboldFont;
height: 18px;
padding: margins(22px, 14px, 22px, 12px);
ripple: defaultRippleAnimation;
}
passportUploadButtonPadding: margins(0px, 10px, 0px, 10px);
passportUploadHeaderPadding: margins(22px, 14px, 22px, 3px);
passportScanNameStyle: TextStyle(defaultTextStyle) {
font: font(boxFontSize semibold);
}
passportScanRow: PassportScanRow {
padding: margins(22px, 10px, 10px, 10px);
size: 40px;
textLeft: 53px;
nameTop: 1px;
statusTop: 22px;
border: 1px;
borderFg: inputBorderFg;
remove: stickersRemove;
restore: stickersUndoRemove;
}
passportScanDeletedOpacity: stickersRowDisabledOpacity;
passportDetailsHeaderPadding: margins(22px, 20px, 33px, 10px);
passportDetailsPadding: margins(22px, 10px, 28px, 10px);
passportDetailsField: InputField(defaultInputField) {
textMargins: margins(2px, 7px, 2px, 0px);
placeholderScale: 0.;
heightMin: 32px;
font: normalFont;
}
passportDetailsFieldLeft: 116px;
passportDetailsFieldTop: 2px;
passportDetailsFieldSkipMin: 12px;

View File

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "passport/passport_edit_identity_box.h"
#include "passport/passport_form_view_separate.h"
#include "passport/passport_panel_controller.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/buttons.h"
#include "ui/text_options.h"
@ -64,7 +64,7 @@ ScanButton::ScanButton(
st::defaultTextStyle,
description,
Ui::NameTextOptions())
, _delete(this, st::passportRowCheckbox) {
, _delete(this, st::passportScanDelete) {
}
void ScanButton::setImage(const QImage &image) {
@ -142,7 +142,7 @@ void ScanButton::paintEvent(QPaintEvent *e) {
IdentityBox::IdentityBox(
QWidget*,
not_null<ViewSeparate*> controller,
not_null<PanelController*> controller,
int valueIndex,
const IdentityData &data,
std::vector<ScanInfo> &&files)

View File

@ -16,7 +16,7 @@ class InputField;
namespace Passport {
class ViewSeparate;
class PanelController;
struct ScanInfo;
class ScanButton;
@ -29,7 +29,7 @@ class IdentityBox : public BoxContent {
public:
IdentityBox(
QWidget*,
not_null<ViewSeparate*> controller,
not_null<PanelController*> controller,
int valueIndex,
const IdentityData &data,
std::vector<ScanInfo> &&files);
@ -49,7 +49,7 @@ private:
void updateControlsPosition();
void save();
not_null<ViewSeparate*> _controller;
not_null<PanelController*> _controller;
int _valueIndex = -1;
std::vector<ScanInfo> _files;

View File

@ -1,264 +0,0 @@
/*
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_form_box.h"
#include "passport/passport_form_view_separate.h"
#include "passport/passport_form_row.h"
#include "lang/lang_keys.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/text_options.h"
#include "styles/style_boxes.h"
#include "styles/style_widgets.h"
#include "styles/style_passport.h"
namespace Passport {
class FormBox::CheckWidget : public Ui::RpWidget {
public:
CheckWidget(QWidget *parent, not_null<ViewSeparate*> controller);
void setInnerFocus();
void submit();
protected:
int resizeGetHeight(int newWidth) override;
private:
void showError(const QString &error);
void hideError();
not_null<ViewSeparate*> _controller;
object_ptr<Ui::PasswordInput> _password;
object_ptr<Ui::FlatLabel> _hint = { nullptr };
object_ptr<Ui::FlatLabel> _error = { nullptr };
object_ptr<Ui::LinkButton> _forgot;
object_ptr<Ui::FlatLabel> _about;
};
class FormBox::Inner : public Ui::RpWidget {
public:
Inner(QWidget *parent, not_null<ViewSeparate*> controller);
void refresh();
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
not_null<ViewSeparate*> _controller;
std::vector<object_ptr<FormRow>> _rows;
};
FormBox::CheckWidget::CheckWidget(
QWidget *parent,
not_null<ViewSeparate*> controller)
: RpWidget(parent)
, _controller(controller)
, _password(
this,
st::defaultInputField,
langFactory(lng_passport_password_placeholder))
, _forgot(this, lang(lng_signin_recover), st::boxLinkButton)
, _about(
this,
lang(lng_passport_password_request),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabel) {
connect(_password, &Ui::PasswordInput::submitted, this, [=] {
submit();
});
connect(_password, &Ui::PasswordInput::changed, this, [=] {
hideError();
});
if (const auto hint = _controller->passwordHint(); !hint.isEmpty()) {
_hint.create(
this,
hint,
Ui::FlatLabel::InitType::Simple,
st::passportPasswordHintLabel);
}
_controller->passwordError(
) | rpl::start_with_next([=](const QString &error) {
showError(error);
}, lifetime());
}
void FormBox::CheckWidget::showError(const QString &error) {
_password->showError();
_error.create(
this,
error,
Ui::FlatLabel::InitType::Simple,
st::passportErrorLabel);
_error->show();
if (_hint) {
_hint->hide();
}
resizeToWidth(width());
}
void FormBox::CheckWidget::hideError() {
_error.destroy();
if (_hint) {
_hint->show();
}
}
void FormBox::CheckWidget::submit() {
_controller->submitPassword(_password->getLastText());
}
void FormBox::CheckWidget::setInnerFocus() {
_password->setFocusFast();
}
int FormBox::CheckWidget::resizeGetHeight(int newWidth) {
const auto padding = st::passportPasswordPadding;
const auto availableWidth = newWidth
- st::boxPadding.left()
- st::boxPadding.right();
auto top = padding.top();
_about->resizeToWidth(availableWidth);
_about->moveToLeft(padding.left(), top);
top += _about->height();
_password->resize(availableWidth, _password->height());
_password->moveToLeft(padding.left(), top);
top += _password->height();
if (_error) {
_error->resizeToWidth(availableWidth);
_error->moveToLeft(padding.left(), top);
}
if (_hint) {
_hint->resizeToWidth(availableWidth);
_hint->moveToLeft(padding.left(), top);
top += _hint->height();
} else {
top += st::passportPasswordHintLabel.style.font->height;
}
_forgot->moveToLeft(padding.left(), top);
top += _forgot->height();
return top + padding.bottom();
}
FormBox::Inner::Inner(
QWidget *parent,
not_null<ViewSeparate*> controller)
: RpWidget(parent)
, _controller(controller) {
refresh();
}
void FormBox::Inner::refresh() {
auto index = 0;
_controller->fillRows([&](
QString title,
QString description,
bool ready) {
if (_rows.size() <= index) {
_rows.push_back(object_ptr<FormRow>(this, title, description));
_rows[index]->addClickHandler([=] {
_controller->editValue(index);
});
}
_rows[index++]->setReady(ready);
});
while (_rows.size() > index) {
_rows.pop_back();
}
resizeToWidth(width());
}
int FormBox::Inner::resizeGetHeight(int newWidth) {
auto result = 0;
for (auto &row : _rows) {
row->resizeToWidth(newWidth);
row->moveToLeft(0, result);
result += row->height();
}
return result;
}
void FormBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
}
FormBox::FormBox(QWidget*, not_null<ViewSeparate*> controller)
: _controller(controller) {
}
void FormBox::prepare() {
setTitle(langFactory(lng_passport_title));
addButton(langFactory(lng_create_group_next), [=] {
submitPassword();
});
addButton(langFactory(lng_cancel), [=] {
closeBox();
});
_passwordCheck = setInnerWidget(
object_ptr<CheckWidget>(this, _controller));
_passwordCheck->resizeToWidth(st::boxWideWidth);
_innerCached.create(this, _controller);
_innerCached->resizeToWidth(st::boxWideWidth);
const auto desiredHeight = std::min(
std::max(_innerCached->height(), _passwordCheck->height()),
st::boxMaxListHeight);
setDimensions(st::boxWideWidth, desiredHeight);
_innerCached->hide();
_controller->secretReadyEvents(
) | rpl::start_with_next([=] {
showForm();
}, lifetime());
}
void FormBox::setInnerFocus() {
if (_passwordCheck) {
_passwordCheck->setInnerFocus();
} else {
_inner->setFocus();
}
}
void FormBox::submitPassword() {
Expects(_passwordCheck != nullptr);
_passwordCheck->submit();
}
void FormBox::showForm() {
clearButtons();
addButton(langFactory(lng_passport_authorize), [=] {
submitForm();
});
addButton(langFactory(lng_cancel), [=] {
closeBox();
});
_inner = setInnerWidget(std::move(_innerCached));
_inner->show();
}
void FormBox::submitForm() {
}
} // namespace Passport

View File

@ -1,39 +0,0 @@
/*
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
*/
#pragma once
#include "boxes/abstract_box.h"
namespace Passport {
class ViewSeparate;
class FormBox : public BoxContent {
public:
FormBox(QWidget*, not_null<ViewSeparate*> controller);
protected:
void prepare() override;
void setInnerFocus() override;
private:
class CheckWidget;
class Inner;
void submitPassword();
void showForm();
void submitForm();
not_null<ViewSeparate*> _controller;
object_ptr<Inner> _innerCached = { nullptr };
QPointer<CheckWidget> _passwordCheck;
QPointer<Inner> _inner;
};
} // namespace Passport

View File

@ -7,14 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "passport/passport_form_controller.h"
#include "passport/passport_form_box.h"
#include "passport/passport_edit_identity_box.h"
#include "passport/passport_encryption.h"
#include "passport/passport_form_view_separate.h"
#include "passport/passport_panel_controller.h"
#include "boxes/confirm_box.h"
#include "lang/lang_keys.h"
#include "base/openssl_help.h"
#include "mainwindow.h"
#include "window/window_controller.h"
#include "auth_session.h"
#include "storage/localimageloader.h"
#include "storage/localstorage.h"
@ -93,8 +92,8 @@ FormController::FormController(
not_null<Window::Controller*> controller,
const FormRequest &request)
: _controller(controller)
, _view(std::make_unique<ViewSeparate>(this))
, _request(request) {
, _request(request)
, _view(std::make_unique<PanelController>(this)) {
}
void FormController::show() {
@ -193,7 +192,7 @@ void FormController::decryptValue(Value &value) {
resetValue(value);
return;
}
value.data.parsed = DeserializeData(DecryptData(
value.data.parsed.fields = DeserializeData(DecryptData(
bytes::make_span(value.data.original),
value.data.hash,
value.data.secret));
@ -220,20 +219,6 @@ bool FormController::validateValueSecrets(Value &value) {
return false;
}
}
const auto fileHashesSecrets = ranges::view::all(
value.files
) | ranges::view::transform([](File &file) {
return bytes::concatenate(file.hash, file.encryptedSecret);
});
const auto countedHash = openssl::Sha256(bytes::concatenate(
value.data.hash,
value.data.encryptedSecret,
bytes::concatenate(fileHashesSecrets)));
if (value.consistencyHash != countedHash) {
LOG(("API Error: Wrong hash after decrypting value secrets. "
"Forgetting files and data :("));
return false;
}
return true;
}
@ -312,15 +297,24 @@ void FormController::encryptScan(
});
}
void FormController::deleteScan(
void FormController::deleteScan(int valueIndex, int fileIndex) {
scanDeleteRestore(valueIndex, fileIndex, true);
}
void FormController::restoreScan(int valueIndex, int fileIndex) {
scanDeleteRestore(valueIndex, fileIndex, false);
}
void FormController::scanDeleteRestore(
int valueIndex,
int fileIndex) {
int fileIndex,
bool deleted) {
Expects(valueIndex >= 0 && valueIndex < _form.rows.size());
Expects(fileIndex >= 0
&& fileIndex < _form.rows[valueIndex].filesInEdit.size());
auto &file = _form.rows[valueIndex].filesInEdit[fileIndex];
file.deleted = !file.deleted;
file.deleted = deleted;
_scanUpdated.fire(&file);
}
@ -590,7 +584,7 @@ void FormController::saveEncryptedValue(int index) {
value.data.secret = GenerateSecretBytes();
}
const auto encryptedData = EncryptData(
SerializeData(value.data.parsed),
SerializeData(value.data.parsed.fields),
value.data.secret);
value.data.hash = encryptedData.hash;
value.data.encryptedSecret = EncryptValueSecret(
@ -598,20 +592,6 @@ void FormController::saveEncryptedValue(int index) {
_secret,
value.data.hash);
const auto fileHashesSecrets = ranges::view::all(
value.filesInEdit
) | ranges::view::filter([](const EditFile &file) {
return !file.deleted;
}) | ranges::view::transform([](const EditFile &file) {
return bytes::concatenate(
file.fields.hash,
file.fields.encryptedSecret);
});
value.consistencyHash = openssl::Sha256(bytes::concatenate(
value.data.hash,
value.data.encryptedSecret,
bytes::concatenate(fileHashesSecrets)));
const auto wrap = [&] {
switch (value.type) {
case Value::Type::Identity: return MTP_inputSecureValueIdentity;
@ -624,8 +604,7 @@ void FormController::saveEncryptedValue(int index) {
MTP_bytes(encryptedData.bytes),
MTP_bytes(value.data.hash),
MTP_bytes(value.data.encryptedSecret)),
MTP_vector<MTPInputSecureFile>(inputFiles),
MTP_bytes(value.consistencyHash)));
MTP_vector<MTPInputSecureFile>(inputFiles)));
}
void FormController::savePlainTextValue(int index) {
@ -633,11 +612,7 @@ void FormController::savePlainTextValue(int index) {
Expects(!isEncryptedValue(_form.rows[index].type));
auto &value = _form.rows[index];
const auto text = value.data.parsed[QString("value")];
QVector<MTPInputSecureFile>();
value.consistencyHash = openssl::Sha256(
bytes::make_span(text.toUtf8()));
const auto text = value.data.parsed.fields[QString("value")];
const auto wrap = [&] {
switch (value.type) {
case Value::Type::Phone: return MTP_inputSecureValuePhone;
@ -645,9 +620,7 @@ void FormController::savePlainTextValue(int index) {
}
Unexpected("Value type in savePlainTextValue().");
}();
sendSaveRequest(index, wrap(
MTP_string(text),
MTP_bytes(value.consistencyHash)));
sendSaveRequest(index, wrap(MTP_string(text)));
}
void FormController::sendSaveRequest(
@ -747,7 +720,6 @@ auto FormController::parseEncryptedValue(
if (data.has_verified()) {
result.verification = parseVerified(data.vverified);
}
result.consistencyHash = bytes::make_vector(data.vhash.v);
const auto &fields = data.vdata.c_secureData();
result.data.original = fields.vdata.v;
result.data.hash = bytes::make_vector(fields.vdata_hash.v);
@ -822,22 +794,10 @@ auto FormController::parsePlainTextValue(
const QByteArray &text,
const DataType &data) const -> Value {
auto result = Value(type);
const auto check = bytes::compare(
bytes::make_span(data.vhash.v),
openssl::Sha256(bytes::make_span(text)));
if (check != 0) {
LOG(("API Error: Bad hash for plain text value. "
"Value '%1', hash '%2'"
).arg(QString::fromUtf8(text)
).arg(Logs::mb(data.vhash.v.data(), data.vhash.v.size()).str()
));
return result;
}
result.data.parsed[QString("value")] = QString::fromUtf8(text);
result.data.parsed.fields[QString("value")] = QString::fromUtf8(text);
if (data.has_verified()) {
result.verification = parseVerified(data.vverified);
}
result.consistencyHash = bytes::make_vector(data.vhash.v);
return result;
}
@ -917,7 +877,7 @@ auto FormController::findFile(const FileKey &key)
void FormController::formDone(const MTPaccount_AuthorizationForm &result) {
parseForm(result);
if (!_passwordRequestId) {
_view->showForm();
showForm();
}
}
@ -963,7 +923,12 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
}
void FormController::formFail(const RPCError &error) {
Ui::show(Box<InformBox>(lang(lng_passport_form_error)));
// #TODO passport testing
_form.rows.push_back(Value(Value::Type::Identity));
_form.rows.back().data.parsed.fields["first_name"] = "test1";
_form.rows.back().data.parsed.fields["last_name"] = "test2";
_view->editValue(0);
// Ui::show(Box<InformBox>(lang(lng_passport_form_error)));
}
void FormController::requestPassword() {
@ -987,7 +952,21 @@ void FormController::passwordDone(const MTPaccount_Password &result) {
break;
}
if (!_formRequestId) {
_view->showForm();
showForm();
}
}
void FormController::showForm() {
if (!_bot) {
Ui::show(Box<InformBox>("Could not get authorization bot."));
return;
}
if (!_password.salt.empty()) {
_view->showAskPassword();
} else if (!_password.unconfirmedPattern.isEmpty()) {
_view->showPasswordUnconfirmed();
} else {
_view->showNoPassword();
}
}
@ -1012,6 +991,19 @@ void FormController::parsePassword(const MTPDaccount_password &result) {
openssl::AddRandomSeed(bytes::make_span(result.vsecure_random.v));
}
void FormController::cancel() {
if (!_cancelled) {
_cancelled = true;
crl::on_main(this, [=] {
_controller->clearAuthForm();
});
}
}
rpl::lifetime &FormController::lifetime() {
return _lifetime;
}
FormController::~FormController() = default;
} // namespace Passport

View File

@ -96,9 +96,13 @@ struct Verification {
TimeId date;
};
struct ValueMap {
std::map<QString, QString> fields;
};
struct ValueData {
QByteArray original;
std::map<QString, QString> parsed;
ValueMap parsed;
bytes::vector hash;
bytes::vector secret;
bytes::vector encryptedSecret;
@ -120,7 +124,6 @@ struct Value {
ValueData data;
std::vector<File> files;
std::vector<EditFile> filesInEdit;
bytes::vector consistencyHash;
base::optional<Verification> verification;
};
@ -178,6 +181,7 @@ public:
void uploadScan(int valueIndex, QByteArray &&content);
void deleteScan(int valueIndex, int fileIndex);
void restoreScan(int valueIndex, int fileIndex);
rpl::producer<> secretReadyEvents() const;
@ -191,9 +195,9 @@ public:
void cancelValueEdit(int index);
void saveValueEdit(int index);
rpl::lifetime &lifetime() {
return _lifetime;
}
void cancel();
rpl::lifetime &lifetime();
~FormController();
@ -259,6 +263,7 @@ private:
void scanUploadDone(const Storage::UploadSecureDone &data);
void scanUploadProgress(const Storage::UploadSecureProgress &data);
void scanUploadFail(const FullMsgId &fullId);
void scanDeleteRestore(int valueIndex, int fileIndex, bool deleted);
bool isEncryptedValue(Value::Type type) const;
void saveEncryptedValue(int index);
@ -266,7 +271,6 @@ private:
void sendSaveRequest(int index, const MTPInputSecureValue &value);
not_null<Window::Controller*> _controller;
std::unique_ptr<ViewController> _view;
FormRequest _request;
UserData *_bot = nullptr;
@ -276,6 +280,7 @@ private:
PasswordSettings _password;
Form _form;
bool _cancelled = false;
std::map<FileKey, std::unique_ptr<mtpFileLoader>> _fileLoaders;
rpl::event_stream<not_null<const EditFile*>> _scanUpdated;
@ -290,6 +295,8 @@ private:
rpl::lifetime _uploaderSubscriptions;
rpl::lifetime _lifetime;
std::unique_ptr<ViewController> _view;
};
} // namespace Passport

View File

@ -32,18 +32,9 @@ FormRow::FormRow(
}
void FormRow::setReady(bool ready) {
if (ready) {
_checkbox.create(this, object_ptr<Ui::IconButton>(
this,
st::passportRowCheckbox));
_checkbox->show(anim::type::instant);
_checkbox->entity()->addClickHandler([=] {
_checkbox->hide(anim::type::normal);
});
} else {
_checkbox.destroy();
}
_ready = ready;
resizeToWidth(width());
update();
}
int FormRow::resizeGetHeight(int newWidth) {
@ -55,13 +46,6 @@ int FormRow::resizeGetHeight(int newWidth) {
+ st::passportRowSkip
+ _descriptionHeight
+ st::passportRowPadding.bottom();
if (_checkbox) {
const auto right = st::passportRowPadding.right();
_checkbox->moveToRight(
right,
(result - _checkbox->height()) / 2,
newWidth);
}
return result;
}
@ -69,7 +53,7 @@ int FormRow::countAvailableWidth(int newWidth) const {
return newWidth
- st::passportRowPadding.left()
- st::passportRowPadding.right()
- (_checkbox ? _checkbox->width() : 0);
- (_ready ? st::passportRowReadyIcon : st::passportRowEmptyIcon).width();
}
int FormRow::countAvailableWidth() const {
@ -86,11 +70,22 @@ void FormRow::paintEvent(QPaintEvent *e) {
const auto availableWidth = countAvailableWidth();
auto top = st::passportRowPadding.top();
p.setPen(st::passportRowTitleFg);
_title.drawLeft(p, left, top, availableWidth, width());
top += _titleHeight + st::passportRowSkip;
p.setPen(st::passportRowDescriptionFg);
_description.drawLeft(p, left, top, availableWidth, width());
top += _descriptionHeight + st::passportRowPadding.bottom();
const auto &icon = _ready
? st::passportRowReadyIcon
: st::passportRowEmptyIcon;
icon.paint(
p,
width() - st::passportRowPadding.right() - icon.width(),
(height() - icon.height()) / 2,
width());
}
} // namespace Passport

View File

@ -16,30 +16,4 @@ class FadeWrapScaled;
namespace Passport {
class FormRow : public Ui::RippleButton {
public:
FormRow(
QWidget *parent,
const QString &title,
const QString &description);
void setReady(bool ready);
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;
object_ptr<Ui::FadeWrapScaled<Ui::IconButton>> _checkbox = { nullptr };
};
} // namespace Passport

View File

@ -13,7 +13,9 @@ struct Value;
class ViewController {
public:
virtual void showForm() = 0;
virtual void showAskPassword() = 0;
virtual void showNoPassword() = 0;
virtual void showPasswordUnconfirmed() = 0;
virtual void editValue(int index) = 0;
virtual ~ViewController() {

View File

@ -0,0 +1,392 @@
/*
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_panel.h"
#include "passport/passport_panel_controller.h"
#include "passport/passport_panel_form.h"
#include "passport/passport_panel_password.h"
#include "window/main_window.h"
#include "platform/platform_specific.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/fade_wrap.h"
#include "lang/lang_keys.h"
#include "messenger.h"
#include "styles/style_passport.h"
#include "styles/style_widgets.h"
#include "styles/style_calls.h"
namespace Passport {
Panel::Panel(not_null<PanelController*> controller)
: _controller(controller)
, _close(this, st::passportPanelClose)
, _title(
this,
lang(lng_passport_title),
Ui::FlatLabel::InitType::Simple,
st::passportPanelTitle)
, _back(this, object_ptr<Ui::IconButton>(this, st::passportPanelBack)) {
setMouseTracking(true);
setWindowIcon(Window::CreateIcon());
initControls();
initLayout();
}
void Panel::initControls() {
widthValue(
) | rpl::start_with_next([=](int width) {
_back->moveToLeft(_padding.left(), _padding.top());
_close->moveToRight(_padding.right(), _padding.top());
_title->resizeToWidth(width
- _padding.left() - _back->width()
- _padding.right() - _close->width());
updateTitlePosition();
}, lifetime());
_close->addClickHandler([=] {
hideAndDestroy();
});
_back->toggledValue(
) | rpl::start_with_next([=](bool toggled) {
_titleLeft.start(
[=] { updateTitlePosition(); },
toggled ? 0. : 1.,
toggled ? 1. : 0.,
st::fadeWrapDuration);
}, _title->lifetime());
_back->hide(anim::type::instant);
_titleLeft.finish();
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
}
void Panel::updateTitlePosition() {
const auto progress = _titleLeft.current(_back->toggled() ? 1. : 0.);
const auto left = anim::interpolate(
st::passportPanelTitleLeft,
_back->width() + st::passportPanelTitleSkip,
progress);
_title->moveToLeft(
_padding.left() + left,
_padding.top() + st::passportPanelTitleTop);
}
rpl::producer<> Panel::backRequests() const {
return _back->entity()->clicks();
}
void Panel::setBackAllowed(bool allowed) {
if (allowed != _back->toggled()) {
_back->toggle(allowed, anim::type::normal);
}
}
void Panel::showAndActivate() {
toggleOpacityAnimation(true);
raise();
setWindowState(windowState() | Qt::WindowActive);
activateWindow();
setFocus();
}
bool Panel::eventHook(QEvent *e) {
if (e->type() == QEvent::WindowDeactivate) {
}
return RpWidget::eventHook(e);
}
void Panel::initLayout() {
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint)
| Qt::WindowStaysOnTopHint
| Qt::BypassWindowManagerHint
| Qt::NoDropShadowWindowHint
| Qt::Dialog);
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
initGeometry();
createBorderImage();
Platform::InitOnTopPanel(this);
}
void Panel::createBorderImage() {
if (!_useTransparency || !_borderParts.isNull()) {
return;
}
const auto cacheSize = st::passportPanelBorderCacheSize;
auto cache = QImage(
cacheSize * cIntRetinaFactor(),
cacheSize * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
cache.setDevicePixelRatio(cRetinaFactor());
cache.fill(Qt::transparent);
{
Painter p(&cache);
auto inner = QRect(0, 0, cacheSize, cacheSize).marginsRemoved(
_padding);
Ui::Shadow::paint(p, inner, width(), st::callShadow);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.setBrush(st::windowBg);
p.setPen(Qt::NoPen);
PainterHighQualityEnabler hq(p);
p.drawRoundedRect(
myrtlrect(inner),
st::callRadius,
st::callRadius);
}
_borderParts = App::pixmapFromImageInPlace(std::move(cache));
}
void Panel::toggleOpacityAnimation(bool visible) {
if (_visible == visible) {
return;
}
_visible = visible;
if (_useTransparency) {
if (_animationCache.isNull()) {
showControls();
_animationCache = Ui::GrabWidget(this);
hideChildren();
}
_opacityAnimation.start(
[this] { opacityCallback(); },
_visible ? 0. : 1.,
_visible ? 1. : 0.,
st::callPanelDuration,
_visible ? anim::easeOutCirc : anim::easeInCirc);
}
if (isHidden() && _visible) {
show();
}
}
void Panel::opacityCallback() {
update();
if (!_visible && !_opacityAnimation.animating()) {
finishAnimating();
}
}
void Panel::finishAnimating() {
_animationCache = QPixmap();
if (_visible) {
showControls();
_inner->setFocus();
} else {
destroyDelayed();
}
}
void Panel::showControls() {
showChildren();
if (!_back->toggled()) {
_back->setVisible(false);
}
}
void Panel::destroyDelayed() {
hide();
_controller->cancelAuth();
}
void Panel::hideAndDestroy() {
toggleOpacityAnimation(false);
if (_animationCache.isNull()) {
destroyDelayed();
}
}
void Panel::showAskPassword() {
showInner(base::make_unique_q<PanelAskPassword>(this, _controller));
}
void Panel::showNoPassword() {
showInner(base::make_unique_q<PanelNoPassword>(this, _controller));
}
void Panel::showPasswordUnconfirmed() {
showInner(base::make_unique_q<PanelPasswordUnconfirmed>(this, _controller));
}
void Panel::showForm() {
showInner(base::make_unique_q<PanelForm>(this, _controller));
}
void Panel::showEditValue(object_ptr<Ui::RpWidget> from) {
showInner(base::unique_qptr<Ui::RpWidget>(from.data()));
}
void Panel::showInner(base::unique_qptr<Ui::RpWidget> inner) {
_inner = std::move(inner);
_inner->setParent(this);
_inner->show();
sizeValue(
) | rpl::start_with_next([=] {
const auto top = _padding.top() + st::passportPanelTitleHeight;
_inner->setGeometry(
_padding.left(),
top,
width() - _padding.left() - _padding.right(),
height() - top - _padding.bottom());
}, _inner->lifetime());
showAndActivate();
if (!_inner->isHidden()) {
_inner->setFocus();
}
}
void Panel::initGeometry() {
auto center = Messenger::Instance().getPointForCallPanelCenter();
_useTransparency = Platform::TranslucentWindowsSupported(center);
setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
_padding = _useTransparency
? st::callShadow.extend
: style::margins(
st::lineWidth,
st::lineWidth,
st::lineWidth,
st::lineWidth);
auto screen = QApplication::desktop()->screenGeometry(center);
auto rect = QRect(0, 0, st::passportPanelWidth, st::passportPanelHeight);
setGeometry(rect.translated(center - rect.center()).marginsAdded(_padding));
updateControlsGeometry();
}
void Panel::resizeEvent(QResizeEvent *e) {
updateControlsGeometry();
}
void Panel::updateControlsGeometry() {
}
void Panel::paintEvent(QPaintEvent *e) {
Painter p(this);
if (!_animationCache.isNull()) {
auto opacity = _opacityAnimation.current(
getms(),
_visible ? 1. : 0.);
if (!_opacityAnimation.animating()) {
finishAnimating();
if (isHidden()) return;
} else {
Platform::StartTranslucentPaint(p, e);
p.setOpacity(opacity);
PainterHighQualityEnabler hq(p);
auto marginRatio = (1. - opacity) / 5;
auto marginWidth = qRound(width() * marginRatio);
auto marginHeight = qRound(height() * marginRatio);
p.drawPixmap(
rect().marginsRemoved(
QMargins(
marginWidth,
marginHeight,
marginWidth,
marginHeight)),
_animationCache,
QRect(QPoint(0, 0), _animationCache.size()));
return;
}
}
if (_useTransparency) {
Platform::StartTranslucentPaint(p, e);
paintShadowBorder(p);
} else {
paintOpaqueBorder(p);
}
}
void Panel::paintShadowBorder(Painter &p) const {
const auto factor = cIntRetinaFactor();
const auto size = st::passportPanelBorderCacheSize;
const auto part1 = size / 3;
const auto part2 = size - part1;
const auto corner = QSize(part1, part1) * factor;
const auto topleft = QRect(QPoint(0, 0), corner);
p.drawPixmap(QRect(0, 0, part1, part1), _borderParts, topleft);
const auto topright = QRect(QPoint(part2, 0) * factor, corner);
p.drawPixmap(QRect(width() - part1, 0, part1, part1), _borderParts, topright);
const auto bottomleft = QRect(QPoint(0, part2) * factor, corner);
p.drawPixmap(QRect(0, height() - part1, part1, part1), _borderParts, bottomleft);
const auto bottomright = QRect(QPoint(part2, part2) * factor, corner);
p.drawPixmap(QRect(width() - part1, height() - part1, part1, part1), _borderParts, bottomright);
const auto left = QRect(QPoint(0, part1) * factor, QSize(_padding.left(), part2 - part1) * factor);
p.drawPixmap(QRect(0, part1, _padding.left(), height() - 2 * part1), _borderParts, left);
const auto top = QRect(QPoint(part1, 0) * factor, QSize(part2 - part1, _padding.top() + st::callRadius) * factor);
p.drawPixmap(QRect(part1, 0, width() - 2 * part1, _padding.top() + st::callRadius), _borderParts, top);
const auto right = QRect(QPoint(size - _padding.right(), part1) * factor, QSize(_padding.right(), part2 - part1) * factor);
p.drawPixmap(QRect(width() - _padding.right(), part1, _padding.right(), height() - 2 * part1), _borderParts, right);
const auto bottom = QRect(QPoint(part1, size - _padding.bottom() - st::callRadius) * factor, QSize(part2 - part1, _padding.bottom() + st::callRadius) * factor);
p.drawPixmap(QRect(part1, height() - _padding.bottom() - st::callRadius, width() - 2 * part1, _padding.bottom() + st::callRadius), _borderParts, bottom);
p.fillRect(_padding.left(), _padding.top() + st::callRadius, width() - _padding.left() - _padding.right(), height() - _padding.top() - _padding.bottom() - 2 * st::callRadius, st::windowBg);
}
void Panel::paintOpaqueBorder(Painter &p) const {
const auto border = st::windowShadowFgFallback;
p.fillRect(0, 0, width(), _padding.top(), border);
p.fillRect(myrtlrect(0, _padding.top(), _padding.left(), height() - _padding.top()), border);
p.fillRect(myrtlrect(width() - _padding.right(), _padding.top(), _padding.right(), height() - _padding.top()), border);
p.fillRect(_padding.left(), height() - _padding.bottom(), width() - _padding.left() - _padding.right(), _padding.bottom(), border);
p.fillRect(_padding.left(), _padding.top(), width() - _padding.left() - _padding.right(), height() - _padding.top() - _padding.bottom(), st::windowBg);
}
void Panel::closeEvent(QCloseEvent *e) {
// #TODO
}
void Panel::mousePressEvent(QMouseEvent *e) {
auto dragArea = myrtlrect(
_padding.left(),
_padding.top(),
width() - _padding.left() - _padding.right(),
st::passportPanelTitleHeight);
if (e->button() == Qt::LeftButton) {
if (dragArea.contains(e->pos())) {
_dragging = true;
_dragStartMousePosition = e->globalPos();
_dragStartMyPosition = QPoint(x(), y());
} else if (!rect().contains(e->pos())) {
}
}
}
void Panel::mouseMoveEvent(QMouseEvent *e) {
if (_dragging) {
if (!(e->buttons() & Qt::LeftButton)) {
_dragging = false;
} else {
move(_dragStartMyPosition + (e->globalPos() - _dragStartMousePosition));
}
}
}
void Panel::mouseReleaseEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
_dragging = false;
}
}
void Panel::leaveEventHook(QEvent *e) {
}
void Panel::leaveToChildEvent(QEvent *e, QWidget *child) {
}
} // namespace Passport

View File

@ -0,0 +1,95 @@
/*
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
*/
#pragma once
#include "ui/rp_widget.h"
namespace Ui {
class IconButton;
class FlatLabel;
template <typename Widget>
class FadeWrapScaled;
} // namespace Ui
namespace Passport {
class PanelController;
class Panel
: public Ui::RpWidget
, private base::Subscriber {
public:
Panel(not_null<PanelController*> controller);
void showAndActivate();
void hideAndDestroy();
void showAskPassword();
void showNoPassword();
void showPasswordUnconfirmed();
void showForm();
void showEditValue(object_ptr<Ui::RpWidget> form);
rpl::producer<> backRequests() const;
void setBackAllowed(bool allowed);
protected:
void paintEvent(QPaintEvent *e) override;
void closeEvent(QCloseEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void leaveEventHook(QEvent *e) override;
void leaveToChildEvent(QEvent *e, QWidget *child) override;
bool eventHook(QEvent *e) override;
private:
void initControls();
void initLayout();
void initGeometry();
void showControls();
void updateControlsGeometry();
void createBorderImage();
void opacityCallback();
void showInner(base::unique_qptr<Ui::RpWidget> inner);
void updateTitlePosition();
void paintShadowBorder(Painter &p) const;
void paintOpaqueBorder(Painter &p) const;
void toggleOpacityAnimation(bool visible);
void finishAnimating();
void destroyDelayed();
not_null<PanelController*> _controller;
object_ptr<Ui::IconButton> _close;
object_ptr<Ui::FlatLabel> _title;
object_ptr<Ui::FadeWrapScaled<Ui::IconButton>> _back;
base::unique_qptr<Ui::RpWidget> _inner;
bool _useTransparency = true;
style::margins _padding;
bool _dragging = false;
QPoint _dragStartMousePosition;
QPoint _dragStartMyPosition;
int _stateChangedSubscription = 0;
Animation _titleLeft;
bool _visible = false;
Animation _opacityAnimation;
QPixmap _animationCache;
QPixmap _borderParts;
};
} // namespace Passport

View File

@ -5,12 +5,13 @@ 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_form_view_separate.h"
#include "passport/passport_panel_controller.h"
#include "lang/lang_keys.h"
#include "passport/passport_edit_identity_box.h"
#include "passport/passport_form_box.h"
#include "passport/passport_panel_edit_identity.h"
#include "passport/passport_panel.h"
#include "boxes/confirm_box.h"
#include "layout.h"
namespace Passport {
@ -49,15 +50,21 @@ BoxContent *BoxPointer::operator->() const {
return get();
}
ViewSeparate::ViewSeparate(not_null<FormController*> form)
PanelController::PanelController(not_null<FormController*> form)
: _form(form) {
_form->secretReadyEvents(
) | rpl::start_with_next([=] {
if (_panel) {
_panel->showForm();
}
}, lifetime());
}
not_null<UserData*> ViewSeparate::bot() const {
not_null<UserData*> PanelController::bot() const {
return _form->bot();
}
void ViewSeparate::fillRows(
void PanelController::fillRows(
base::lambda<void(
QString title,
QString description,
@ -92,130 +99,143 @@ void ViewSeparate::fillRows(
});
}
void ViewSeparate::submitPassword(const QString &password) {
void PanelController::submitPassword(const QString &password) {
_form->submitPassword(password);
}
rpl::producer<QString> ViewSeparate::passwordError() const {
rpl::producer<QString> PanelController::passwordError() const {
return _form->passwordError();
}
QString ViewSeparate::passwordHint() const {
QString PanelController::passwordHint() const {
return _form->passwordHint();
}
rpl::producer<> ViewSeparate::secretReadyEvents() const {
return _form->secretReadyEvents();
}
QString ViewSeparate::defaultEmail() const {
QString PanelController::defaultEmail() const {
return _form->defaultEmail();
}
QString ViewSeparate::defaultPhoneNumber() const {
QString PanelController::defaultPhoneNumber() const {
return _form->defaultPhoneNumber();
}
void ViewSeparate::uploadScan(int valueIndex, QByteArray &&content) {
Expects(_editBox != nullptr);
void PanelController::uploadScan(int valueIndex, QByteArray &&content) {
Expects(_panel != nullptr);
_form->uploadScan(valueIndex, std::move(content));
}
void ViewSeparate::deleteScan(int valueIndex, int fileIndex) {
Expects(_editBox != nullptr);
void PanelController::deleteScan(int valueIndex, int fileIndex) {
Expects(_panel != nullptr);
_form->deleteScan(valueIndex, fileIndex);
}
rpl::producer<ScanInfo> ViewSeparate::scanUpdated() const {
void PanelController::restoreScan(int valueIndex, int fileIndex) {
Expects(_panel != nullptr);
_form->restoreScan(valueIndex, fileIndex);
}
rpl::producer<ScanInfo> PanelController::scanUpdated() const {
return _form->scanUpdated(
) | rpl::map([=](not_null<const EditFile*> file) {
return collectScanInfo(*file);
});
}
ScanInfo ViewSeparate::collectScanInfo(const EditFile &file) const {
ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
const auto status = [&] {
if (file.deleted) {
return QString("deleted");
} else if (file.fields.accessHash) {
if (file.fields.accessHash) {
if (file.fields.downloadOffset < 0) {
return QString("download failed");
return lang(lng_attach_failed);
} else if (file.fields.downloadOffset < file.fields.size) {
return QString("downloading %1 / %2"
).arg(file.fields.downloadOffset
).arg(file.fields.size);
return formatDownloadText(
file.fields.downloadOffset,
file.fields.size);
} else {
return QString("uploaded ")
+ langDateTimeFull(ParseDateTime(file.fields.date));
return lng_passport_scan_uploaded(
lt_date,
langDateTimeFull(ParseDateTime(file.fields.date)));
}
} else if (file.uploadData) {
if (file.uploadData->offset < 0) {
return QString("upload failed");
return lang(lng_attach_failed);
} else if (file.uploadData->fullId) {
return QString("uploading %1 / %2"
).arg(file.uploadData->offset
).arg(file.uploadData->bytes.size());
return formatDownloadText(
file.uploadData->offset,
file.uploadData->bytes.size());
} else {
return QString("upload ready");
return lng_passport_scan_uploaded(
lt_date,
langDateTimeFull(ParseDateTime(file.fields.date)));
}
} else {
return QString("preparing");
return formatDownloadText(0, file.fields.size);
}
}();
return {
FileKey{ file.fields.id, file.fields.dcId },
status,
file.fields.image };
file.fields.image,
file.deleted };
}
void ViewSeparate::showForm() {
if (!_form->bot()) {
Ui::show(Box<InformBox>("Could not get authorization bot."));
return;
void PanelController::showAskPassword() {
ensurePanelCreated();
_panel->showAskPassword();
}
void PanelController::showNoPassword() {
ensurePanelCreated();
_panel->showNoPassword();
}
void PanelController::showPasswordUnconfirmed() {
ensurePanelCreated();
_panel->showPasswordUnconfirmed();
}
void PanelController::ensurePanelCreated() {
if (!_panel) {
_panel = std::make_unique<Panel>(this);
}
Ui::show(Box<FormBox>(this));
}
void ViewSeparate::editValue(int index) {
void PanelController::editValue(int index) {
ensurePanelCreated(); // #TODO passport testing
Expects(_panel != nullptr);
_editValue = _form->startValueEdit(index);
Assert(_editValue != nullptr);
auto box = [&]() -> object_ptr<BoxContent> {
auto content = [&]() -> object_ptr<Ui::RpWidget> {
switch (_editValue->type) {
case Value::Type::Identity:
return Box<IdentityBox>(
return object_ptr<PanelEditIdentity>(
_panel.get(),
this,
index,
valueDataIdentity(*_editValue),
_editValue->data.parsed,
valueFiles(*_editValue));
}
return { nullptr };
}();
if (box) {
_editBox = Ui::show(std::move(box), LayerOption::KeepOther);
_editBox->boxClosing() | rpl::start_with_next([=] {
if (content) {
_panel->setBackAllowed(true);
_panel->backRequests(
) | rpl::start_with_next([=] {
cancelValueEdit(index);
}, _form->lifetime());
_panel->setBackAllowed(false);
_panel->showForm();
}, content->lifetime());
_panel->showEditValue(std::move(content));
} else {
cancelValueEdit(index);
}
}
IdentityData ViewSeparate::valueDataIdentity(const Value &value) const {
const auto &map = value.data.parsed;
auto result = IdentityData();
if (const auto i = map.find(qsl("first_name")); i != map.cend()) {
result.name = i->second;
}
if (const auto i = map.find(qsl("last_name")); i != map.cend()) {
result.surname = i->second;
}
return result;
}
std::vector<ScanInfo> ViewSeparate::valueFiles(const Value &value) const {
std::vector<ScanInfo> PanelController::valueFiles(const Value &value) const {
auto result = std::vector<ScanInfo>();
for (const auto &file : value.filesInEdit) {
result.push_back(collectScanInfo(file));
@ -223,26 +243,30 @@ std::vector<ScanInfo> ViewSeparate::valueFiles(const Value &value) const {
return result;
}
void ViewSeparate::cancelValueEdit(int index) {
void PanelController::cancelValueEdit(int index) {
if (base::take(_editValue)) {
_form->cancelValueEdit(index);
}
}
void ViewSeparate::saveValueIdentity(
int index,
const IdentityData &data) {
Expects(_editBox != nullptr);
void PanelController::saveValue(int index, ValueMap &&data) {
Expects(_panel != nullptr);
Expects(_editValue != nullptr);
Expects(_editValue->type == Value::Type::Identity);
_editValue->data.parsed[qsl("first_name")] = data.name;
_editValue->data.parsed[qsl("last_name")] = data.surname;
_editValue->data.parsed = std::move(data);
_editValue = nullptr;
_editBox->closeBox();
_panel->showForm();
_form->saveValueEdit(index);
}
void PanelController::cancelAuth() {
_form->cancel();
}
rpl::lifetime &PanelController::lifetime() {
return _lifetime;
}
} // namespace Passport

View File

@ -13,13 +13,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Passport {
class FormController;
struct IdentityData;
class Panel;
struct ScanInfo {
FileKey key;
QString status;
QImage thumb;
bool deleted = false;
};
@ -40,9 +40,9 @@ private:
};
class ViewSeparate : public ViewController {
class PanelController : public ViewController {
public:
ViewSeparate(not_null<FormController*> form);
PanelController(not_null<FormController*> form);
not_null<UserData*> bot() const;
@ -52,14 +52,16 @@ public:
void uploadScan(int valueIndex, QByteArray &&content);
void deleteScan(int valueIndex, int fileIndex);
void restoreScan(int valueIndex, int fileIndex);
rpl::producer<ScanInfo> scanUpdated() const;
rpl::producer<> secretReadyEvents() const;
QString defaultEmail() const;
QString defaultPhoneNumber() const;
void showForm() override;
void showAskPassword() override;
void showNoPassword() override;
void showPasswordUnconfirmed() override;
void fillRows(
base::lambda<void(
QString title,
@ -67,19 +69,26 @@ public:
bool ready)> callback);
void editValue(int index) override;
void saveValueIdentity(int index, const IdentityData &data);
void saveValue(int index, ValueMap &&data);
void cancelAuth();
rpl::lifetime &lifetime();
private:
void ensurePanelCreated();
void cancelValueEdit(int index);
IdentityData valueDataIdentity(const Value &value) const;
std::vector<ScanInfo> valueFiles(const Value &value) const;
ScanInfo collectScanInfo(const EditFile &file) const;
not_null<FormController*> _form;
std::unique_ptr<Panel> _panel;
Value *_editValue = nullptr;
BoxPointer _editBox;
rpl::lifetime _lifetime;
};

View File

@ -0,0 +1,46 @@
/*
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_panel_details_row.h"
#include "ui/widgets/input_fields.h"
#include "styles/style_passport.h"
namespace Passport {
PanelDetailsRow::PanelDetailsRow(
QWidget *parent,
const QString &label,
const QString &value)
: _label(label)
, _field(this, st::passportDetailsField) {
}
QPointer<Ui::InputField> PanelDetailsRow::field() const {
return _field.data();
}
int PanelDetailsRow::resizeGetHeight(int newWidth) {
const auto padding = st::passportDetailsPadding;
const auto inputLeft = padding.left() + st::passportDetailsFieldLeft;
const auto inputTop = st::passportDetailsFieldTop;
const auto inputRight = padding.right();
const auto inputWidth = std::max(newWidth - inputLeft - inputRight, 0);
_field->setGeometry(inputLeft, inputTop, inputWidth, _field->height());
return padding.top() + st::semiboldFont->height + padding.bottom();
}
void PanelDetailsRow::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setFont(st::semiboldFont);
p.setPen(st::passportDetailsField.placeholderFg);
const auto padding = st::passportDetailsPadding;
p.drawTextLeft(padding.left(), padding.top(), width(), _label);
}
} // namespace Passport

View File

@ -0,0 +1,38 @@
/*
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
*/
#pragma once
#include "ui/rp_widget.h"
namespace Ui {
class InputField;
} // namespace Ui
namespace Passport {
class PanelDetailsRow : public Ui::RpWidget {
public:
PanelDetailsRow(
QWidget *parent,
const QString &label,
const QString &value);
QPointer<Ui::InputField> field() const;
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
QString _label;
object_ptr<Ui::InputField> _field;
};
} // namespace Passport

View File

@ -0,0 +1,413 @@
/*
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_panel_edit_identity.h"
#include "passport/passport_panel_controller.h"
#include "passport/passport_panel_details_row.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/text_options.h"
#include "info/profile/info_profile_button.h"
#include "info/profile/info_profile_values.h"
#include "boxes/abstract_box.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::AbstractButton {
public:
ScanButton(
QWidget *parent,
const style::PassportScanRow &st,
const QString &name,
const QString &status,
bool deleted);
void setImage(const QImage &image);
void setStatus(const QString &status);
void setDeleted(bool deleted);
rpl::producer<> deleteClicks() const {
return _delete->entity()->clicks();
}
rpl::producer<> restoreClicks() const {
return _restore->entity()->clicks();
}
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
int countAvailableWidth() const;
const style::PassportScanRow &_st;
Text _name;
Text _status;
int _nameHeight = 0;
int _statusHeight = 0;
QImage _image;
object_ptr<Ui::FadeWrapScaled<Ui::IconButton>> _delete;
object_ptr<Ui::FadeWrapScaled<Ui::RoundButton>> _restore;
};
ScanButton::ScanButton(
QWidget *parent,
const style::PassportScanRow &st,
const QString &name,
const QString &status,
bool deleted)
: AbstractButton(parent)
, _st(st)
, _name(
st::passportScanNameStyle,
name,
Ui::NameTextOptions())
, _status(
st::defaultTextStyle,
status,
Ui::NameTextOptions())
, _delete(this, object_ptr<Ui::IconButton>(this, _st.remove))
, _restore(
this,
object_ptr<Ui::RoundButton>(
this,
langFactory(lng_passport_delete_scan_undo),
_st.restore)) {
_delete->toggle(!deleted, anim::type::instant);
_restore->toggle(deleted, anim::type::instant);
}
void ScanButton::setImage(const QImage &image) {
_image = image;
update();
}
void ScanButton::setStatus(const QString &status) {
_status.setText(
st::defaultTextStyle,
status,
Ui::NameTextOptions());
update();
}
void ScanButton::setDeleted(bool deleted) {
_delete->toggle(!deleted, anim::type::instant);
_restore->toggle(deleted, anim::type::instant);
update();
}
int ScanButton::resizeGetHeight(int newWidth) {
_nameHeight = st::semiboldFont->height;
_statusHeight = st::normalFont->height;
const auto result = _st.padding.top() + _st.size + _st.padding.bottom();
const auto right = _st.padding.right();
_delete->moveToRight(
right,
(result - _delete->height()) / 2,
newWidth);
_restore->moveToRight(
right,
(result - _restore->height()) / 2,
newWidth);
return result + st::lineWidth;
}
int ScanButton::countAvailableWidth() const {
return width()
- _st.padding.left()
- _st.textLeft
- _st.padding.right()
- std::max(_delete->width(), _restore->width());
}
void ScanButton::paintEvent(QPaintEvent *e) {
Painter p(this);
const auto left = _st.padding.left();
const auto top = _st.padding.top();
p.fillRect(
left,
height() - _st.border,
width() - left,
_st.border,
_st.borderFg);
if (_restore->toggled()) {
p.setOpacity(st::passportScanDeletedOpacity);
}
if (_image.isNull()) {
p.fillRect(left, top, _st.size, _st.size, Qt::black);
} else {
PainterHighQualityEnabler hq(p);
const auto fromRect = [&] {
if (_image.width() > _image.height()) {
const auto shift = (_image.width() - _image.height()) / 2;
return QRect(shift, 0, _image.height(), _image.height());
} else {
const auto shift = (_image.height() - _image.width()) / 2;
return QRect(0, shift, _image.width(), _image.width());
}
}();
p.drawImage(QRect(left, top, _st.size, _st.size), _image, fromRect);
}
const auto availableWidth = countAvailableWidth();
p.setPen(st::windowFg);
_name.drawLeftElided(
p,
left + _st.textLeft,
top + _st.nameTop,
availableWidth,
width());
p.setPen(st::windowSubTextFg);
_status.drawLeftElided(
p,
left + _st.textLeft,
top + _st.statusTop,
availableWidth,
width());
}
PanelEditIdentity::PanelEditIdentity(
QWidget*,
not_null<PanelController*> controller,
int valueIndex,
const ValueMap &data,
std::vector<ScanInfo> &&files)
: _controller(controller)
, _valueIndex(valueIndex)
, _files(std::move(files))
, _scroll(this, st::passportPanelScroll)
, _topShadow(this)
, _bottomShadow(this)
, _done(
this,
langFactory(lng_passport_save_value),
st::passportPanelSaveValue) {
setupControls(data);
}
void PanelEditIdentity::setupControls(const ValueMap &data) {
const auto inner = setupContent(data);
using namespace rpl::mappers;
_topShadow->toggleOn(
_scroll->scrollTopValue() | rpl::map(_1 > 0));
_done->addClickHandler([=] {
crl::on_main(this, [=] {
save();
});
});
_controller->scanUpdated(
) | rpl::start_with_next([=](ScanInfo &&info) {
updateScan(std::move(info));
}, lifetime());
}
not_null<Ui::RpWidget*> PanelEditIdentity::setupContent(
const ValueMap &data) {
const auto inner = _scroll->setOwnedWidget(
object_ptr<Ui::VerticalLayout>(this));
_scroll->widthValue(
) | rpl::start_with_next([=](int width) {
inner->resizeToWidth(width);
}, inner->lifetime());
_scansDivider = inner->add(
object_ptr<Ui::SlideWrap<BoxContentDivider>>(
inner,
object_ptr<BoxContentDivider>(
inner,
st::passportFormDividerHeight)));
_scansDivider->toggle(_files.empty(), anim::type::instant);
_scansHeader = inner->add(
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
inner,
object_ptr<Ui::FlatLabel>(
inner,
lang(lng_passport_upload_header),
Ui::FlatLabel::InitType::Simple,
st::passportFormHeader),
st::passportUploadHeaderPadding));
_scansHeader->toggle(!_files.empty(), anim::type::instant);
_scansWrap = inner->add(object_ptr<Ui::VerticalLayout>(inner));
for (const auto &scan : _files) {
pushScan(scan);
_scans.back()->show(anim::type::instant);
}
_scansUpload = inner->add(
object_ptr<Info::Profile::Button>(
inner,
uploadButtonText(),
st::passportUploadButton),
st::passportUploadButtonPadding);
_scansUpload->addClickHandler([=] {
chooseScan();
});
inner->add(object_ptr<BoxContentDivider>(
inner,
st::passportFormDividerHeight));
inner->add(
object_ptr<Ui::FlatLabel>(
inner,
lang(lng_passport_personal_details),
Ui::FlatLabel::InitType::Simple,
st::passportFormHeader),
st::passportDetailsHeaderPadding);
const auto valueOrEmpty = [&](const QString &key) {
if (const auto i = data.fields.find(key); i != data.fields.end()) {
return i->second;
}
return QString();
};
_firstName = inner->add(object_ptr<PanelDetailsRow>(
inner,
lang(lng_passport_first_name),
valueOrEmpty("first_name")))->field();
_lastName = inner->add(object_ptr<PanelDetailsRow>(
inner,
lang(lng_passport_last_name),
valueOrEmpty("last_name")))->field();
return inner;
}
void PanelEditIdentity::updateScan(ScanInfo &&info) {
const auto i = ranges::find(_files, info.key, [](const ScanInfo &file) {
return file.key;
});
if (i != _files.end()) {
*i = info;
const auto scan = _scans[i - _files.begin()]->entity();
scan->setStatus(i->status);
scan->setImage(i->thumb);
scan->setDeleted(i->deleted);
} else {
_files.push_back(std::move(info));
pushScan(_files.back());
_scansWrap->resizeToWidth(width());
_scans.back()->show(anim::type::normal);
_scansDivider->hide(anim::type::normal);
_scansHeader->show(anim::type::normal);
}
}
void PanelEditIdentity::pushScan(const ScanInfo &info) {
const auto index = _scans.size();
_scans.push_back(base::unique_qptr<Ui::SlideWrap<ScanButton>>(
_scansWrap->add(object_ptr<Ui::SlideWrap<ScanButton>>(
_scansWrap,
object_ptr<ScanButton>(
_scansWrap,
st::passportScanRow,
lng_passport_scan_index(lt_index, QString::number(index + 1)),
info.status,
info.deleted)))));
_scans.back()->hide(anim::type::instant);
const auto scan = _scans.back()->entity();
scan->setImage(info.thumb);
scan->deleteClicks(
) | rpl::start_with_next([=] {
_controller->deleteScan(_valueIndex, index);
}, scan->lifetime());
scan->restoreClicks(
) | rpl::start_with_next([=] {
_controller->restoreScan(_valueIndex, index);
}, scan->lifetime());
}
void PanelEditIdentity::focusInEvent(QFocusEvent *e) {
_firstName->setFocusFast();
}
void PanelEditIdentity::resizeEvent(QResizeEvent *e) {
updateControlsGeometry();
}
void PanelEditIdentity::updateControlsGeometry() {
const auto submitTop = height() - _done->height();
_scroll->setGeometry(0, 0, width(), submitTop);
_topShadow->resizeToWidth(width());
_topShadow->moveToLeft(0, 0);
_bottomShadow->resizeToWidth(width());
_bottomShadow->moveToLeft(0, submitTop - st::lineWidth);
_done->resizeToWidth(width());
_done->moveToLeft(0, submitTop);
_scroll->updateBars();
}
void PanelEditIdentity::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(
lang(lng_passport_choose_image),
filter,
base::lambda_guarded(this, callback));
}
void PanelEditIdentity::encryptScan(const QString &path) {
encryptScanContent([&] {
QFile f(path);
if (!f.open(QIODevice::ReadOnly)) {
return QByteArray();
}
return f.readAll();
}());
}
void PanelEditIdentity::encryptScanContent(QByteArray &&content) {
_controller->uploadScan(_valueIndex, std::move(content));
}
void PanelEditIdentity::save() {
auto data = ValueMap();
data.fields["first_name"] = _firstName->getLastText();
data.fields["last_name"] = _lastName->getLastText();
_controller->saveValue(_valueIndex, std::move(data));
}
rpl::producer<QString> PanelEditIdentity::uploadButtonText() const {
return Lang::Viewer(_files.empty()
? lng_passport_upload_scans
: lng_passport_upload_more) | Info::Profile::ToUpperValue();
}
} // namespace Passport

View File

@ -0,0 +1,88 @@
/*
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
*/
#pragma once
#include "ui/rp_widget.h"
class BoxContentDivider;
namespace Ui {
class InputField;
class VerticalLayout;
class ScrollArea;
class FadeShadow;
class PlainShadow;
class FlatLabel;
class RoundButton;
template <typename Widget>
class SlideWrap;
} // namespace Ui
namespace Info {
namespace Profile {
class Button;
} // namespace Profile
} // namespace Info
namespace Passport {
class PanelController;
struct ValueMap;
struct ScanInfo;
class ScanButton;
class PanelEditIdentity : public Ui::RpWidget {
public:
PanelEditIdentity(
QWidget *parent,
not_null<PanelController*> controller,
int valueIndex,
const ValueMap &data,
std::vector<ScanInfo> &&files);
protected:
void focusInEvent(QFocusEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
void setupControls(const ValueMap &data);
not_null<Ui::RpWidget*> setupContent(const ValueMap &data);
void updateControlsGeometry();
void chooseScan();
void encryptScan(const QString &path);
void encryptScanContent(QByteArray &&content);
void updateScan(ScanInfo &&info);
void pushScan(const ScanInfo &info);
rpl::producer<QString> uploadButtonText() const;
void save();
not_null<PanelController*> _controller;
int _valueIndex = -1;
std::vector<ScanInfo> _files;
object_ptr<Ui::ScrollArea> _scroll;
object_ptr<Ui::FadeShadow> _topShadow;
object_ptr<Ui::PlainShadow> _bottomShadow;
QPointer<Ui::SlideWrap<BoxContentDivider>> _scansDivider;
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _scansHeader;
QPointer<Ui::VerticalLayout> _scansWrap;
std::vector<base::unique_qptr<Ui::SlideWrap<ScanButton>>> _scans;
QPointer<Info::Profile::Button> _scansUpload;
QPointer<Ui::InputField> _firstName;
QPointer<Ui::InputField> _lastName;
object_ptr<Ui::RoundButton> _done;
};
} // namespace Passport

View File

@ -0,0 +1,266 @@
/*
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_panel_form.h"
#include "passport/passport_panel_controller.h"
#include "lang/lang_keys.h"
#include "boxes/abstract_box.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/text_options.h"
#include "ui/special_buttons.h"
#include "styles/style_passport.h"
#include "styles/style_boxes.h"
namespace Passport {
class PanelForm::Row : public Ui::RippleButton {
public:
Row(
QWidget *parent,
const QString &title,
const QString &description);
void setReady(bool ready);
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;
bool _ready = false;
};
PanelForm::Row::Row(
QWidget *parent,
const QString &title,
const QString &description)
: RippleButton(parent, st::passportRowRipple)
, _title(
st::semiboldTextStyle,
title,
Ui::NameTextOptions(),
st::boxWideWidth / 2)
, _description(
st::defaultTextStyle,
description,
Ui::NameTextOptions(),
st::boxWideWidth / 2) {
}
void PanelForm::Row::setReady(bool ready) {
_ready = ready;
resizeToWidth(width());
update();
}
int PanelForm::Row::resizeGetHeight(int newWidth) {
const auto availableWidth = countAvailableWidth(newWidth);
_titleHeight = _title.countHeight(availableWidth);
_descriptionHeight = _description.countHeight(availableWidth);
const auto result = st::passportRowPadding.top()
+ _titleHeight
+ st::passportRowSkip
+ _descriptionHeight
+ st::passportRowPadding.bottom();
return result;
}
int PanelForm::Row::countAvailableWidth(int newWidth) const {
return newWidth
- st::passportRowPadding.left()
- st::passportRowPadding.right()
- (_ready ? st::passportRowReadyIcon : st::passportRowEmptyIcon).width();
}
int PanelForm::Row::countAvailableWidth() const {
return countAvailableWidth(width());
}
void PanelForm::Row::paintEvent(QPaintEvent *e) {
Painter p(this);
const auto ms = getms();
paintRipple(p, 0, 0, ms);
const auto left = st::passportRowPadding.left();
const auto availableWidth = countAvailableWidth();
auto top = st::passportRowPadding.top();
p.setPen(st::passportRowTitleFg);
_title.drawLeft(p, left, top, availableWidth, width());
top += _titleHeight + st::passportRowSkip;
p.setPen(st::passportRowDescriptionFg);
_description.drawLeft(p, left, top, availableWidth, width());
top += _descriptionHeight + st::passportRowPadding.bottom();
const auto &icon = _ready
? st::passportRowReadyIcon
: st::passportRowEmptyIcon;
icon.paint(
p,
width() - st::passportRowPadding.right() - icon.width(),
(height() - icon.height()) / 2,
width());
}
PanelForm::PanelForm(
QWidget *parent,
not_null<PanelController*> controller)
: RpWidget(parent)
, _controller(controller)
, _scroll(this, st::passportPanelScroll)
, _topShadow(this)
, _bottomShadow(this)
, _submit(
this,
langFactory(lng_passport_authorize),
st::passportPanelAuthorize) {
setupControls();
}
void PanelForm::setupControls() {
const auto inner = setupContent();
using namespace rpl::mappers;
_topShadow->toggleOn(
_scroll->scrollTopValue() | rpl::map(_1 > 0));
_bottomShadow->toggleOn(rpl::combine(
_scroll->scrollTopValue(),
_scroll->heightValue(),
inner->heightValue(),
_1 + _2 < _3));
}
not_null<Ui::RpWidget*> PanelForm::setupContent() {
const auto bot = _controller->bot();
const auto inner = _scroll->setOwnedWidget(
object_ptr<Ui::VerticalLayout>(this));
_scroll->widthValue(
) | rpl::start_with_next([=](int width) {
inner->resizeToWidth(width);
}, inner->lifetime());
const auto userpicWrap = inner->add(
object_ptr<Ui::FixedHeightWidget>(
inner,
st::passportFormUserpic.size.height()),
st::passportFormUserpicPadding);
_userpic = Ui::AttachParentChild(
userpicWrap,
object_ptr<Ui::UserpicButton>(
userpicWrap,
bot,
Ui::UserpicButton::Role::Custom,
st::passportFormUserpic));
userpicWrap->widthValue(
) | rpl::start_with_next([=](int width) {
_userpic->move((width - _userpic->width()) / 2, _userpic->y());
}, _userpic->lifetime());
auto about1 = object_ptr<Ui::FlatLabel>(
inner,
lng_passport_request1(lt_bot, App::peerName(bot)),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabelBold);
_about1 = about1.data();
inner->add(
object_ptr<Ui::IgnoreNaturalWidth>(inner, std::move(about1)),
st::passportFormAbout1Padding);
auto about2 = object_ptr<Ui::FlatLabel>(
inner,
lang(lng_passport_request2),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabel);
_about2 = about2.data();
inner->add(
object_ptr<Ui::IgnoreNaturalWidth>(inner, std::move(about2)),
st::passportFormAbout2Padding);
inner->add(object_ptr<BoxContentDivider>(
inner,
st::passportFormDividerHeight));
inner->add(
object_ptr<Ui::FlatLabel>(
inner,
lang(lng_passport_header),
Ui::FlatLabel::InitType::Simple,
st::passportFormHeader),
st::passportFormHeaderPadding);
auto index = 0;
_controller->fillRows([&](
QString title,
QString description,
bool ready) {
_rows.push_back(inner->add(object_ptr<Row>(
this,
title,
description)));
_rows.back()->addClickHandler([=] {
_controller->editValue(index);
});
_rows.back()->setReady(ready);
});
const auto policy = inner->add(
object_ptr<Ui::FlatLabel>(
inner,
lng_passport_accept_allow(
lt_policy,
textcmdLink(
1,
lng_passport_policy(lt_bot, App::peerName(bot))),
lt_bot,
'@' + bot->username),
Ui::FlatLabel::InitType::Rich,
st::passportFormPolicy),
st::passportFormPolicyPadding);
policy->setLink(1, std::make_shared<LambdaClickHandler>([=] {
_controller->cancelAuth();
// #TODO passport policy
}));
return inner;
}
void PanelForm::resizeEvent(QResizeEvent *e) {
updateControlsGeometry();
}
void PanelForm::updateControlsGeometry() {
const auto submitTop = height() - _submit->height();
_scroll->setGeometry(0, 0, width(), submitTop);
_topShadow->resizeToWidth(width());
_topShadow->moveToLeft(0, 0);
_bottomShadow->resizeToWidth(width());
_bottomShadow->moveToLeft(0, submitTop - st::lineWidth);
_submit->resizeToWidth(width());
_submit->moveToLeft(0, submitTop);
_scroll->updateBars();
}
} // namespace Passport

View File

@ -0,0 +1,56 @@
/*
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
*/
#pragma once
#include "ui/rp_widget.h"
class BoxContentDivider;
namespace Ui {
class ScrollArea;
class FadeShadow;
class RoundButton;
class FlatLabel;
class UserpicButton;
} // namespace Ui
namespace Passport {
class PanelController;
class PanelForm : public Ui::RpWidget {
public:
PanelForm(
QWidget *parent,
not_null<PanelController*> controller);
protected:
void resizeEvent(QResizeEvent *e) override;
private:
class Row;
void setupControls();
not_null<Ui::RpWidget*> setupContent();
void updateControlsGeometry();
not_null<PanelController*> _controller;
object_ptr<Ui::ScrollArea> _scroll;
object_ptr<Ui::FadeShadow> _topShadow;
object_ptr<Ui::FadeShadow> _bottomShadow;
object_ptr<Ui::RoundButton> _submit;
QPointer<Ui::UserpicButton> _userpic;
QPointer<Ui::FlatLabel> _about1;
QPointer<Ui::FlatLabel> _about2;
std::vector<QPointer<Row>> _rows;
};
} // namespace Passport

View File

@ -0,0 +1,163 @@
/*
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_panel_password.h"
#include "passport/passport_panel_controller.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/special_buttons.h"
#include "lang/lang_keys.h"
#include "styles/style_passport.h"
#include "styles/style_boxes.h"
namespace Passport {
PanelAskPassword::PanelAskPassword(
QWidget *parent,
not_null<PanelController*> controller)
: RpWidget(parent)
, _controller(controller)
, _userpic(
this,
_controller->bot(),
Ui::UserpicButton::Role::Custom,
st::passportPasswordUserpic)
, _about1(
this,
lng_passport_request1(lt_bot, App::peerName(_controller->bot())),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabelBold)
, _about2(
this,
lang(lng_passport_request2),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabel)
, _password(
this,
st::defaultInputField,
langFactory(lng_passport_password_placeholder))
, _submit(this, langFactory(lng_passport_next), st::passportPasswordSubmit)
, _forgot(this, lang(lng_signin_recover), st::defaultLinkButton) {
connect(_password, &Ui::PasswordInput::submitted, this, [=] {
submit();
});
connect(_password, &Ui::PasswordInput::changed, this, [=] {
hideError();
});
if (const auto hint = _controller->passwordHint(); !hint.isEmpty()) {
_hint.create(
this,
hint,
Ui::FlatLabel::InitType::Simple,
st::passportPasswordHintLabel);
}
_controller->passwordError(
) | rpl::start_with_next([=](const QString &error) {
showError(error);
}, lifetime());
_password->setFocusFast();
_userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
_submit->addClickHandler([=] {
submit();
});
}
void PanelAskPassword::showError(const QString &error) {
_password->showError();
_error.create(
this,
error,
Ui::FlatLabel::InitType::Simple,
st::passportErrorLabel);
_error->show();
updateControlsGeometry();
}
void PanelAskPassword::hideError() {
_error.destroy();
}
void PanelAskPassword::submit() {
_controller->submitPassword(_password->getLastText());
}
void PanelAskPassword::setInnerFocus() {
_password->setFocusFast();
}
void PanelAskPassword::resizeEvent(QResizeEvent *e) {
updateControlsGeometry();
}
void PanelAskPassword::focusInEvent(QFocusEvent *e) {
_password->setFocusFast();
}
void PanelAskPassword::updateControlsGeometry() {
const auto padding = st::passportPasswordPadding;
const auto availableWidth = width()
- st::boxPadding.left()
- st::boxPadding.right();
auto top = st::passportPasswordFieldBottom;
top -= _password->height();
_password->resize(
st::passportPasswordSubmit.width,
_password->height());
_password->moveToLeft((width() - _password->width()) / 2, top);
top -= st::passportPasswordFieldSkip + _about2->height();
_about2->resizeToWidth(availableWidth);
_about2->moveToLeft(padding.left(), top);
top -= _about1->height();
_about1->resizeToWidth(availableWidth);
_about1->moveToLeft(padding.left(), top);
top -= st::passportPasswordUserpicSkip + _userpic->height();
_userpic->moveToLeft((width() - _userpic->width()) / 2, top);
top = st::passportPasswordFieldBottom;
if (_hint) {
top += st::passportPasswordHintSkip;
_hint->resizeToWidth(availableWidth);
_hint->moveToLeft(padding.left(), top);
top += _hint->height();
}
if (_error) {
top += st::passportPasswordHintSkip;
_error->resizeToWidth(availableWidth);
_error->moveToLeft(padding.left(), top);
top += _error->height();
}
top = height() - st::passportPasswordSubmitBottom - _submit->height();
_submit->moveToLeft((width() - _submit->width()) / 2, top);
top = height() - st::passportPasswordForgotBottom - _forgot->height();
_forgot->moveToLeft((width() - _forgot->width()) / 2, top);
}
PanelNoPassword::PanelNoPassword(
QWidget *parent,
not_null<PanelController*> controller)
: RpWidget(parent)
, _controller(controller) {
}
PanelPasswordUnconfirmed::PanelPasswordUnconfirmed(
QWidget *parent,
not_null<PanelController*> controller)
: RpWidget(parent)
, _controller(controller) {
}
} // namespace Passport

View File

@ -0,0 +1,77 @@
/*
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
*/
#pragma once
#include "ui/rp_widget.h"
namespace Ui {
class PasswordInput;
class FlatLabel;
class LinkButton;
class RoundButton;
class UserpicButton;
} // namespace Ui
namespace Passport {
class PanelController;
class PanelAskPassword : public Ui::RpWidget {
public:
PanelAskPassword(
QWidget *parent,
not_null<PanelController*> controller);
void setInnerFocus();
void submit();
protected:
void resizeEvent(QResizeEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
private:
void updateControlsGeometry();
void showError(const QString &error);
void hideError();
not_null<PanelController*> _controller;
object_ptr<Ui::UserpicButton> _userpic;
object_ptr<Ui::FlatLabel> _about1;
object_ptr<Ui::FlatLabel> _about2;
object_ptr<Ui::PasswordInput> _password;
object_ptr<Ui::FlatLabel> _hint = { nullptr };
object_ptr<Ui::FlatLabel> _error = { nullptr };
object_ptr<Ui::RoundButton> _submit;
object_ptr<Ui::LinkButton> _forgot;
};
class PanelNoPassword : public Ui::RpWidget {
public:
PanelNoPassword(
QWidget *parent,
not_null<PanelController*> controller);
private:
not_null<PanelController*> _controller;
};
class PanelPasswordUnconfirmed : public Ui::RpWidget {
public:
PanelPasswordUnconfirmed(
QWidget *parent,
not_null<PanelController*> controller);
private:
not_null<PanelController*> _controller;
};
} // namespace Passport

View File

@ -416,6 +416,22 @@ UserpicButton::UserpicButton(
setupPeerViewers();
}
UserpicButton::UserpicButton(
QWidget *parent,
not_null<PeerData*> peer,
Role role,
const style::UserpicButton &st)
: RippleButton(parent, st.changeButton.ripple)
, _st(st)
, _peer(peer)
, _peerForCrop(_peer->id)
, _role(role) {
Expects(_role == Role::Custom);
_waiting = false;
prepare();
}
void UserpicButton::prepare() {
resize(_st.size);
_notShownYet = _waiting;

View File

@ -162,6 +162,11 @@ public:
not_null<PeerData*> peer,
Role role,
const style::UserpicButton &st);
UserpicButton(
QWidget *parent,
not_null<PeerData*> peer,
Role role,
const style::UserpicButton &st);
void switchChangePhotoOverlay(bool enabled);
void showSavedMessagesOnSelf(bool enabled);

View File

@ -785,8 +785,8 @@ void ScrollArea::rangeChanged(int oldMax, int newMax, bool vertical) {
}
void ScrollArea::updateBars() {
_horizontalBar->update();
_verticalBar->update();
_horizontalBar->updateBar(true);
_verticalBar->updateBar(true);
}
bool ScrollArea::focusNextPrevChild(bool next) {

View File

@ -525,6 +525,18 @@ InfoProfileButton {
ripple: RippleAnimation;
}
PassportScanRow {
padding: margins;
size: pixels;
textLeft: pixels;
nameTop: pixels;
statusTop: pixels;
border: pixels;
borderFg: color;
remove: IconButton;
restore: RoundButton;
}
defaultLabelSimple: LabelSimple {
font: normalFont;
maxWidth: 0px;

View File

@ -183,4 +183,24 @@ private:
};
class IgnoreNaturalWidth : public Wrap<RpWidget> {
using Parent = Wrap<RpWidget>;
public:
IgnoreNaturalWidth(QWidget *parent, object_ptr<RpWidget> &&child)
: Parent(parent, std::move(child)) {
if (auto weak = wrapped()) {
auto margins = weak->getMargins();
resizeToWidth(weak->width()
- margins.left()
- margins.right());
}
}
int naturalWidth() const override {
return -1;
}
};
} // namespace Ui

View File

@ -410,6 +410,10 @@ void Controller::showAuthForm(const Passport::FormRequest &request) {
_authForm->show();
}
void Controller::clearAuthForm() {
_authForm = nullptr;
}
void Controller::updateColumnLayout() {
App::main()->updateColumnLayout();
}

View File

@ -206,6 +206,7 @@ public:
QDate requestedDate);
void showAuthForm(const Passport::FormRequest &request);
void clearAuthForm();
base::Variable<bool> &dialogsListFocused() {
return _dialogsListFocused;

View File

@ -454,19 +454,23 @@
<(src_loc)/mtproto/type_utils.h
<(src_loc)/overview/overview_layout.cpp
<(src_loc)/overview/overview_layout.h
<(src_loc)/passport/passport_edit_identity_box.cpp
<(src_loc)/passport/passport_edit_identity_box.h
<(src_loc)/passport/passport_encryption.cpp
<(src_loc)/passport/passport_encryption.h
<(src_loc)/passport/passport_form_box.cpp
<(src_loc)/passport/passport_form_box.h
<(src_loc)/passport/passport_form_controller.cpp
<(src_loc)/passport/passport_form_controller.h
<(src_loc)/passport/passport_form_row.cpp
<(src_loc)/passport/passport_form_row.h
<(src_loc)/passport/passport_form_view_controller.h
<(src_loc)/passport/passport_form_view_separate.cpp
<(src_loc)/passport/passport_form_view_separate.h
<(src_loc)/passport/passport_panel.cpp
<(src_loc)/passport/passport_panel.h
<(src_loc)/passport/passport_panel_controller.cpp
<(src_loc)/passport/passport_panel_controller.h
<(src_loc)/passport/passport_panel_details_row.cpp
<(src_loc)/passport/passport_panel_details_row.h
<(src_loc)/passport/passport_panel_edit_identity.cpp
<(src_loc)/passport/passport_panel_edit_identity.h
<(src_loc)/passport/passport_panel_form.cpp
<(src_loc)/passport/passport_panel_form.h
<(src_loc)/passport/passport_panel_password.cpp
<(src_loc)/passport/passport_panel_password.h
<(src_loc)/platform/linux/linux_desktop_environment.cpp
<(src_loc)/platform/linux/linux_desktop_environment.h
<(src_loc)/platform/linux/linux_gdk_helper.cpp