Allow 2sv password setup in passport.

This commit is contained in:
John Preston 2018-04-16 21:02:40 +04:00
parent 5b615519e8
commit 9f6130cd20
21 changed files with 359 additions and 104 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1516,6 +1516,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passport_title" = "Telegram passport";
"lng_passport_request1" = "{bot} requests access to your personal data";
"lng_passport_request2" = "to sign you up for their services";
"lng_passport_create_password" = "Please create a password which will be used\nto encrypt your personal data.";
"lng_passport_about_password" = "This password will also be required whenever\nyou log in to a new device.";
"lng_passport_password_create" = "Create a password";
"lng_passport_link_sent" = "Confirmation link was sent to your email\n{email}";
"lng_passport_stop_password_sure" = "Are you sure you want to stop your password setup?";
"lng_passport_password_placeholder" = "Your password";
"lng_passport_next" = "Next";
"lng_passport_password_wrong" = "The password you entered is not valid.";

View File

@ -227,8 +227,9 @@ void BoxContent::paintEvent(QPaintEvent *e) {
}
}
AbstractBox::AbstractBox(QWidget *parent, object_ptr<BoxContent> content)
: LayerWidget(parent)
AbstractBox::AbstractBox(not_null<Window::LayerStackWidget*> layer, object_ptr<BoxContent> content)
: LayerWidget(layer)
, _layer(layer)
, _content(std::move(content)) {
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
_content->setParent(this);
@ -330,6 +331,13 @@ bool AbstractBox::hasTitle() const {
return (_title != nullptr) || !_additionalTitle.isEmpty();
}
void AbstractBox::showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated) {
_layer->showBox(std::move(box), options, animated);
}
void AbstractBox::updateSize() {
setDimensions(width(), _maxContentHeight);
}

View File

@ -23,6 +23,8 @@ class FlatLabel;
class FadeShadow;
} // namespace Ui
class BoxContent;
class BoxContentDelegate {
public:
virtual void setLayerType(bool layerType) = 0;
@ -34,11 +36,25 @@ public:
virtual QPointer<Ui::RoundButton> addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) = 0;
virtual void updateButtonsPositions() = 0;
virtual void showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated) = 0;
virtual void setDimensions(int newWidth, int maxHeight) = 0;
virtual void setNoContentMargin(bool noContentMargin) = 0;
virtual bool isBoxShown() const = 0;
virtual void closeBox() = 0;
template <typename BoxType>
QPointer<BoxType> show(
object_ptr<BoxType> content,
LayerOptions options = LayerOption::KeepOther,
anim::type animated = anim::type::normal) {
auto result = QPointer<BoxType>(content.data());
showBox(std::move(content), options, animated);
return result;
}
};
class BoxContent : public Ui::RpWidget, protected base::Subscriber {
@ -163,6 +179,10 @@ protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
not_null<BoxContentDelegate*> getDelegate() const {
return _delegate;
}
private slots:
void onScroll();
void onInnerResize();
@ -179,10 +199,6 @@ private:
void updateShadowsVisibility();
object_ptr<TWidget> doTakeInnerWidget();
BoxContentDelegate *getDelegate() const {
Expects(_delegate != nullptr);
return _delegate;
}
BoxContentDelegate *_delegate = nullptr;
bool _preparing = false;
@ -205,13 +221,19 @@ class AbstractBox
, public BoxContentDelegate
, protected base::Subscriber {
public:
AbstractBox(QWidget *parent, object_ptr<BoxContent> content);
AbstractBox(
not_null<Window::LayerStackWidget*> layer,
object_ptr<BoxContent> content);
void parentResized() override;
void setLayerType(bool layerType) override;
void setTitle(base::lambda<TextWithEntities()> titleFactory) override;
void setAdditionalTitle(base::lambda<QString()> additionalFactory) override;
void showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated) override;
void clearButtons() override;
QPointer<Ui::RoundButton> addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) override;
@ -262,6 +284,7 @@ private:
int countRealHeight() const;
void updateSize();
not_null<Window::LayerStackWidget*> _layer;
int _fullHeight = 0;
bool _noContentMargin = false;

View File

@ -190,7 +190,7 @@ void PasscodeBox::setPasswordDone(const MTPBool &result) {
_setRequest = 0;
emit reloadPassword();
auto text = lang(_reenterPasscode->isHidden() ? lng_cloud_password_removed : (_oldPasscode->isHidden() ? lng_cloud_password_was_set : lng_cloud_password_updated));
Ui::show(Box<InformBox>(text));
getDelegate()->show(Box<InformBox>(text), LayerOption::CloseOther);
}
void PasscodeBox::closeReplacedBy() {
@ -244,7 +244,9 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
_recoverEmail->showError();
update();
} else if (err == qstr("EMAIL_UNCONFIRMED")) {
Ui::show(Box<InformBox>(lang(lng_cloud_password_almost)));
getDelegate()->show(
Box<InformBox>(lang(lng_cloud_password_almost)),
LayerOption::CloseOther);
emit reloadPassword();
}
return true;
@ -307,9 +309,9 @@ void PasscodeBox::save(bool force) {
}
if (!_recoverEmail->isHidden() && email.isEmpty() && !force) {
_skipEmailWarning = true;
_replacedBy = Ui::show(Box<ConfirmBox>(lang(lng_cloud_password_about_recover), lang(lng_cloud_password_skip_email), st::attentionBoxButton, base::lambda_guarded(this, [this] {
_replacedBy = getDelegate()->show(Box<ConfirmBox>(lang(lng_cloud_password_about_recover), lang(lng_cloud_password_skip_email), st::attentionBoxButton, base::lambda_guarded(this, [this] {
save(true);
})), LayerOption::KeepOther);
})));
} else {
QByteArray newPasswordData = pwd.isEmpty() ? QByteArray() : (_newSalt + pwd.toUtf8() + _newSalt);
QByteArray newPasswordHash = pwd.isEmpty() ? QByteArray() : QByteArray(32, Qt::Uninitialized);
@ -407,9 +409,7 @@ void PasscodeBox::recoverExpired() {
void PasscodeBox::recover() {
if (_pattern == "-") return;
const auto box = Ui::show(
Box<RecoverBox>(_pattern),
LayerOption::KeepOther);
const auto box = getDelegate()->show(Box<RecoverBox>(_pattern));
connect(box, &RecoverBox::reloadPassword, this, &PasscodeBox::reloadPassword);
connect(box, &RecoverBox::recoveryExpired, this, &PasscodeBox::recoverExpired);
_replacedBy = box;
@ -494,7 +494,9 @@ void RecoverBox::codeSubmitDone(bool recover, const MTPauth_Authorization &resul
_submitRequest = 0;
emit reloadPassword();
Ui::show(Box<InformBox>(lang(lng_cloud_password_removed)));
getDelegate()->show(
Box<InformBox>(lang(lng_cloud_password_removed)),
LayerOption::CloseOther);
}
bool RecoverBox::codeSubmitFail(const RPCError &error) {
@ -512,7 +514,9 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) {
const QString &err = error.type();
if (err == qstr("PASSWORD_EMPTY")) {
emit reloadPassword();
Ui::show(Box<InformBox>(lang(lng_cloud_password_removed)));
getDelegate()->show(
Box<InformBox>(lang(lng_cloud_password_removed)),
LayerOption::CloseOther);
return true;
} else if (err == qstr("PASSWORD_RECOVERY_NA")) {
closeBox();

View File

@ -26,6 +26,9 @@ passportPasswordLabelBold: FlatLabel(passportPasswordLabel) {
linkFontOver: font(boxFontSize semibold underline);
}
}
passportPasswordSetupLabel: FlatLabel(passportPasswordLabel) {
minWidth: 0px;
}
passportPasswordHintLabel: passportPasswordLabel;
passportErrorLabel: FlatLabel(passportPasswordLabel) {
textFg: boxTextFgError;
@ -223,3 +226,8 @@ passportDetailsSkip: 30px;
passportDetailsGenderSkip: 30px;
passportRequestTypeSkip: 16px;
passportPasswordAbout1Padding: margins(10px, 28px, 10px, 0px);
passportPasswordAbout2Padding: margins(10px, 0px, 10px, 28px);
passportPasswordIconHeight: 224px;
passportPasswordIcon: icon {{ "passport_password_setup", windowSubTextFg }};

View File

@ -388,6 +388,34 @@ void FormController::submitPassword(const QString &password) {
}).send();
}
void FormController::reloadPassword() {
requestPassword();
}
void FormController::cancelPassword() {
if (_passwordRequestId) {
return;
}
_passwordRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(QByteArray()),
MTP_account_passwordInputSettings(
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
MTP_bytes(QByteArray()), // new_salt
MTP_bytes(QByteArray()), // new_password_hash
MTP_string(QString()), // hint
MTP_string(QString()), // email
MTP_bytes(QByteArray()), // new_secure_salt
MTP_bytes(QByteArray()), // new_secure_secret
MTP_long(0)) // new_secure_secret_hash
)).done([=](const MTPBool &result) {
_passwordRequestId = 0;
reloadPassword();
}).fail([=](const RPCError &error) {
_passwordRequestId = 0;
reloadPassword();
}).send();
}
void FormController::validateSecureSecret(
bytes::const_span salt,
bytes::const_span encryptedSecret,
@ -525,8 +553,8 @@ rpl::producer<QString> FormController::passwordError() const {
return _passwordError.events();
}
QString FormController::passwordHint() const {
return _password.hint;
const PasswordSettings &FormController::passwordSettings() const {
return _password;
}
void FormController::uploadScan(
@ -1702,6 +1730,9 @@ void FormController::formFail(const QString &error) {
}
void FormController::requestPassword() {
if (_passwordRequestId) {
return;
}
_passwordRequestId = request(MTPaccount_GetPassword(
)).done([=](const MTPaccount_Password &result) {
_passwordRequestId = 0;
@ -1733,14 +1764,13 @@ void FormController::showForm() {
}
if (!_password.salt.empty()) {
_view->showAskPassword();
} else if (!_password.unconfirmedPattern.isEmpty()) {
_view->showPasswordUnconfirmed();
} else {
_view->showNoPassword();
}
}
void FormController::parsePassword(const MTPDaccount_noPassword &result) {
_password = PasswordSettings();
_password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
_password.newSalt = bytes::make_vector(result.vnew_salt.v);
_password.newSecureSalt = bytes::make_vector(result.vnew_secure_salt.v);
@ -1748,6 +1778,7 @@ void FormController::parsePassword(const MTPDaccount_noPassword &result) {
}
void FormController::parsePassword(const MTPDaccount_password &result) {
_password = PasswordSettings();
_password.hint = qs(result.vhint);
_password.hasRecovery = mtpIsTrue(result.vhas_recovery);
_password.salt = bytes::make_vector(result.vcurrent_salt.v);

View File

@ -217,7 +217,9 @@ public:
std::vector<not_null<const Value*>> submitGetErrors();
void submitPassword(const QString &password);
rpl::producer<QString> passwordError() const;
QString passwordHint() const;
const PasswordSettings &passwordSettings() const;
void reloadPassword();
void cancelPassword();
bool canAddScan(not_null<const Value*> value) const;
void uploadScan(not_null<const Value*> value, QByteArray &&content);

View File

@ -42,11 +42,13 @@ class ViewController {
public:
virtual void showAskPassword() = 0;
virtual void showNoPassword() = 0;
virtual void showPasswordUnconfirmed() = 0;
virtual void showCriticalError(const QString &error) = 0;
virtual void editScope(int index) = 0;
virtual void showBox(object_ptr<BoxContent> box) = 0;
virtual void showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated) = 0;
virtual void showToast(const QString &text) = 0;
virtual void suggestReset(base::lambda<void()> callback) = 0;
@ -56,9 +58,12 @@ public:
}
template <typename BoxType>
QPointer<BoxType> show(object_ptr<BoxType> content) {
auto result = QPointer<BoxType>(content.data());
showBox(std::move(content));
QPointer<BoxType> show(
object_ptr<BoxType> box,
LayerOptions options = LayerOption::KeepOther,
anim::type animated = anim::type::normal) {
auto result = QPointer<BoxType>(box.data());
showBox(std::move(box), options, animated);
return result;
}

View File

@ -226,12 +226,6 @@ void Panel::showNoPassword() {
setBackAllowed(false);
}
void Panel::showPasswordUnconfirmed() {
showInner(
base::make_unique_q<PanelPasswordUnconfirmed>(_body, _controller));
setBackAllowed(false);
}
void Panel::showCriticalError(const QString &error) {
auto container = base::make_unique_q<Ui::PaddingWrap<Ui::FlatLabel>>(
_body,
@ -258,12 +252,12 @@ void Panel::showEditValue(object_ptr<Ui::RpWidget> from) {
showInner(base::unique_qptr<Ui::RpWidget>(from.data()));
}
void Panel::showBox(object_ptr<BoxContent> box) {
void Panel::showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated) {
ensureLayerCreated();
_layer->showBox(
std::move(box),
LayerOption::KeepOther,
anim::type::normal);
_layer->showBox(std::move(box), options, animated);
}
void Panel::ensureLayerCreated() {

View File

@ -37,11 +37,13 @@ public:
void showAskPassword();
void showNoPassword();
void showPasswordUnconfirmed();
void showForm();
void showCriticalError(const QString &error);
void showEditValue(object_ptr<Ui::RpWidget> form);
void showBox(object_ptr<BoxContent> box);
void showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated);
rpl::producer<> backRequests() const;
void setBackAllowed(bool allowed);

View File

@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "passport/passport_panel_edit_contact.h"
#include "passport/passport_panel_edit_scans.h"
#include "passport/passport_panel.h"
#include "base/openssl_help.h"
#include "boxes/passcode_box.h"
#include "boxes/confirm_box.h"
#include "ui/toast/toast.h"
#include "ui/countryinput.h"
@ -403,7 +405,11 @@ rpl::producer<QString> PanelController::passwordError() const {
}
QString PanelController::passwordHint() const {
return _form->passwordHint();
return _form->passwordSettings().hint;
}
QString PanelController::unconfirmedEmailPattern() const {
return _form->passwordSettings().unconfirmedPattern;
}
QString PanelController::defaultEmail() const {
@ -414,6 +420,40 @@ QString PanelController::defaultPhoneNumber() const {
return _form->defaultPhoneNumber();
}
void PanelController::setupPassword() {
Expects(_panel != nullptr);
const auto &settings = _form->passwordSettings();
Assert(settings.salt.empty());
constexpr auto kRandomPart = 8;
auto newSalt = QByteArray(
reinterpret_cast<const char*>(settings.newSalt.data()),
settings.newSalt.size());
newSalt.resize(newSalt.size() + kRandomPart);
bytes::set_random(
bytes::make_span(newSalt).subspan(settings.newSalt.size()));
const auto currentSalt = QByteArray();
const auto hasRecovery = false;
const auto hint = QString();
auto box = show(Box<PasscodeBox>(
newSalt,
currentSalt,
hasRecovery,
hint));
box->connect(box, &PasscodeBox::reloadPassword, _panel.get(), [=] {
_form->reloadPassword();
});
}
void PanelController::cancelPasswordSubmit() {
const auto box = std::make_shared<QPointer<BoxContent>>();
*box = show(Box<ConfirmBox>(
lang(lng_passport_stop_password_sure),
lang(lng_passport_stop),
[=] { if (*box) (*box)->closeBox(); _form->cancelPassword(); }));
}
bool PanelController::canAddScan() const {
Expects(_editScope != nullptr);
Expects(_editDocument != nullptr);
@ -683,11 +723,6 @@ void PanelController::showNoPassword() {
_panel->showNoPassword();
}
void PanelController::showPasswordUnconfirmed() {
ensurePanelCreated();
_panel->showPasswordUnconfirmed();
}
void PanelController::showCriticalError(const QString &error) {
ensurePanelCreated();
_panel->showCriticalError(error);
@ -1060,8 +1095,11 @@ void PanelController::cancelAuth() {
_form->cancel();
}
void PanelController::showBox(object_ptr<BoxContent> box) {
_panel->showBox(std::move(box));
void PanelController::showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated) {
_panel->showBox(std::move(box), options, animated);
}
void PanelController::showToast(const QString &text) {

View File

@ -69,6 +69,10 @@ public:
void submitPassword(const QString &password);
rpl::producer<QString> passwordError() const;
QString passwordHint() const;
QString unconfirmedEmailPattern() const;
void setupPassword();
void cancelPasswordSubmit();
bool canAddScan() const;
void uploadScan(QByteArray &&content);
@ -88,7 +92,6 @@ public:
void showAskPassword() override;
void showNoPassword() override;
void showPasswordUnconfirmed() override;
void showCriticalError(const QString &error) override;
void fillRows(
@ -106,7 +109,10 @@ public:
const ValueMap &filesData) const;
void cancelEditScope();
void showBox(object_ptr<BoxContent> box) override;
void showBox(
object_ptr<BoxContent> box,
LayerOptions options,
anim::type animated) override;
void showToast(const QString &text) override;
void suggestReset(base::lambda<void()> callback) override;

View File

@ -220,25 +220,25 @@ not_null<Ui::RpWidget*> PanelForm::setupContent() {
_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);
_about1 = inner->add(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
inner,
object_ptr<Ui::FlatLabel>(
inner,
lng_passport_request1(lt_bot, App::peerName(bot)),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabelBold)),
st::passportFormAbout1Padding)->entity();
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);
_about2 = inner->add(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
inner,
object_ptr<Ui::FlatLabel>(
inner,
lang(lng_passport_request2),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabel)),
st::passportFormAbout2Padding)->entity();
inner->add(object_ptr<BoxContentDivider>(
inner,

View File

@ -11,8 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/special_buttons.h"
#include "lang/lang_keys.h"
#include "info/profile/info_profile_icon.h"
#include "styles/style_passport.h"
#include "styles/style_boxes.h"
@ -148,14 +151,96 @@ PanelNoPassword::PanelNoPassword(
QWidget *parent,
not_null<PanelController*> controller)
: RpWidget(parent)
, _controller(controller) {
, _controller(controller)
, _inner(Ui::CreateChild<Ui::VerticalLayout>(this)) {
setupContent();
}
PanelPasswordUnconfirmed::PanelPasswordUnconfirmed(
QWidget *parent,
not_null<PanelController*> controller)
: RpWidget(parent)
, _controller(controller) {
void PanelNoPassword::setupContent() {
widthValue(
) | rpl::start_with_next([=](int newWidth) {
_inner->resizeToWidth(newWidth);
}, _inner->lifetime());
const auto about1 = _inner->add(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
_inner,
object_ptr<Ui::FlatLabel>(
_inner,
lng_passport_request1(
lt_bot,
App::peerName(_controller->bot())),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabelBold)),
st::passportPasswordAbout1Padding)->entity();
const auto about2 = _inner->add(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
_inner,
object_ptr<Ui::FlatLabel>(
_inner,
lang(lng_passport_request2),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabel)),
st::passportPasswordAbout2Padding)->entity();
const auto iconWrap = _inner->add(
object_ptr<Ui::CenterWrap<Ui::FixedHeightWidget>>(
_inner,
object_ptr<Ui::FixedHeightWidget>(
_inner,
st::passportPasswordIconHeight)));
iconWrap->entity()->resizeToWidth(st::passportPasswordIcon.width());
Ui::CreateChild<Info::Profile::FloatingIcon>(
iconWrap->entity(),
st::passportPasswordIcon,
QPoint(0, 0));
const auto about3 = _inner->add(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
_inner,
object_ptr<Ui::FlatLabel>(
_inner,
lang(lng_passport_create_password),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordSetupLabel)),
st::passportFormAbout2Padding)->entity();
refreshBottom();
}
void PanelNoPassword::refreshBottom() {
const auto pattern = _controller->unconfirmedEmailPattern();
_about.reset(_inner->add(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
_inner,
object_ptr<Ui::FlatLabel>(
_inner,
(pattern.isEmpty()
? lang(lng_passport_about_password)
: lng_passport_link_sent(lt_email, pattern)),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordSetupLabel)),
st::passportFormAbout2Padding)->entity());
const auto button = _inner->add(
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
_inner,
object_ptr<Ui::RoundButton>(
_inner,
langFactory(pattern.isEmpty()
? lng_passport_password_create
: lng_cancel),
st::defaultBoxButton)));
if (pattern.isEmpty()) {
button->entity()->addClickHandler([=] {
_controller->setupPassword();
});
} else {
button->entity()->addClickHandler([=] {
_controller->cancelPasswordSubmit();
});
}
_button.reset(button);
}
} // namespace Passport

View File

@ -15,6 +15,7 @@ class FlatLabel;
class LinkButton;
class RoundButton;
class UserpicButton;
class VerticalLayout;
} // namespace Ui
namespace Passport {
@ -58,18 +59,14 @@ public:
not_null<PanelController*> controller);
private:
void setupContent();
void refreshBottom();
not_null<PanelController*> _controller;
};
class PanelPasswordUnconfirmed : public Ui::RpWidget {
public:
PanelPasswordUnconfirmed(
QWidget *parent,
not_null<PanelController*> controller);
private:
not_null<PanelController*> _controller;
not_null<Ui::VerticalLayout*> _inner;
base::unique_qptr<Ui::RpWidget> _about;
base::unique_qptr<Ui::RpWidget> _button;
};

View File

@ -67,4 +67,35 @@ int PaddingWrap<RpWidget>::resizeGetHeight(int newWidth) {
return heightNoMargins();
}
CenterWrap<RpWidget>::CenterWrap(
QWidget *parent,
object_ptr<RpWidget> &&child)
: Parent(parent, std::move(child)) {
if (const auto weak = wrapped()) {
wrappedSizeUpdated(weak->size());
}
}
int CenterWrap<RpWidget>::naturalWidth() const {
return -1;
}
int CenterWrap<RpWidget>::resizeGetHeight(int newWidth) {
updateWrappedPosition(newWidth);
return heightNoMargins();
}
void CenterWrap<RpWidget>::wrappedSizeUpdated(QSize size) {
updateWrappedPosition(width());
}
void CenterWrap<RpWidget>::updateWrappedPosition(int forWidth) {
if (const auto weak = wrapped()) {
const auto margins = weak->getMargins();
weak->moveToLeft(
(forWidth - weak->width()) / 2 + margins.left(),
margins.top());
}
}
} // namespace Ui

View File

@ -54,6 +54,42 @@ public:
};
template <typename Widget = RpWidget>
class CenterWrap;
template <>
class CenterWrap<RpWidget> : public Wrap<RpWidget> {
using Parent = Wrap<RpWidget>;
public:
CenterWrap(
QWidget *parent,
object_ptr<RpWidget> &&child);
int naturalWidth() const override;
protected:
int resizeGetHeight(int newWidth) override;
void wrappedSizeUpdated(QSize size) override;
private:
void updateWrappedPosition(int forWidth);
};
template <typename Widget>
class CenterWrap : public Wrap<Widget, CenterWrap<RpWidget>> {
using Parent = Wrap<Widget, CenterWrap<RpWidget>>;
public:
CenterWrap(
QWidget *parent,
object_ptr<Widget> &&child)
: Parent(parent, std::move(child)) {
}
};
class FixedHeightWidget : public RpWidget {
public:
FixedHeightWidget(QWidget *parent, int height)

View File

@ -183,24 +183,4 @@ 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

@ -686,7 +686,7 @@ LayerWidget *LayerStackWidget::pushBox(
if (_layers.size() > 1) {
if (!_background->animating()) {
layer->show();
layer->setVisible(true);
showFinished();
}
} else {