Add EditPeerInfoBox without saving.

This commit is contained in:
John Preston 2017-11-10 19:45:10 +04:00
parent 3998fad7ef
commit 8ff0120642
21 changed files with 1207 additions and 67 deletions

View File

@ -825,6 +825,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_group_invite_create" = "Create an invite link";
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";
"lng_group_invite_about_channel" = "Telegram users will be able to join\nyour channel by following this link.";
"lng_group_invite_create_new" = "Revoke invite link";
"lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you.";
"lng_group_invite_copied" = "Invite link copied to clipboard.";

View File

@ -78,7 +78,7 @@ public:
Return operator()(OtherArgs &&...args) {
return _guard
? _callable(std::forward<OtherArgs>(args)...)
: Return{};
: Return();
}
template <
@ -87,7 +87,7 @@ public:
Return operator()(OtherArgs &&...args) const {
return _guard
? _callable(std::forward<OtherArgs>(args)...)
: Return{};
: Return();
}
private:

View File

@ -126,7 +126,7 @@ boxMediumSkip: 20px;
boxButtonPadding: margins(8px, 12px, 13px, 12px);
boxLayerButtonPadding: margins(8px, 8px, 8px, 8px);
boxLabel: FlatLabel(defaultFlatLabel) {
width: 285px;
minWidth: 285px;
align: align(topleft);
style: boxLabelStyle;
}
@ -159,7 +159,7 @@ cropMinSize: 20px;
confirmInviteTitle: FlatLabel(defaultFlatLabel) {
align: align(center);
width: 320px;
minWidth: 320px;
maxHeight: 24px;
textFg: windowBoldFg;
style: TextStyle(defaultTextStyle) {
@ -170,7 +170,7 @@ confirmInviteTitle: FlatLabel(defaultFlatLabel) {
}
confirmInviteStatus: FlatLabel(boxLabel) {
align: align(center);
width: 320px;
minWidth: 320px;
maxHeight: 20px;
textFg: windowSubTextFg;
}
@ -183,13 +183,13 @@ confirmInviteUserPhotoSize: 56px;
confirmInviteUserPhotoTop: 166px;
confirmInviteUserName: FlatLabel(defaultFlatLabel) {
align: align(center);
width: 66px;
minWidth: 66px;
maxHeight: 20px;
}
confirmInviteUserNameTop: 227px;
confirmPhoneAboutLabel: FlatLabel(defaultFlatLabel) {
width: 282px;
minWidth: 282px;
}
confirmPhoneCodeField: InputField(defaultInputField) {
}
@ -199,7 +199,7 @@ revokePublicLinkStatusPalette: TextPalette(defaultTextPalette) {
}
aboutRevokePublicLabel: FlatLabel(defaultFlatLabel) {
align: align(topleft);
width: 320px;
minWidth: 320px;
}
editBioCountdownLabel: FlatLabel(defaultFlatLabel) {
style: boxTextStyle;
@ -394,7 +394,7 @@ notificationSampleSize: size(64px, 16px);
membersAboutLimitPadding: margins(0px, 12px, 0px, 12px);
membersAbout: FlatLabel(defaultFlatLabel) {
width: 332px;
minWidth: 332px;
textFg: membersAboutLimitFg;
align: align(top);
style: boxLabelStyle;
@ -490,7 +490,7 @@ aboutVersionLink: LinkButton(defaultLinkButton) {
aboutTextTop: 34px;
aboutSkip: 14px;
aboutLabel: FlatLabel(defaultFlatLabel) {
width: 330px;
minWidth: 330px;
align: align(topleft);
style: TextStyle(defaultTextStyle) {
lineHeight: 22px;
@ -601,7 +601,7 @@ editPrivacyOptionMargin: margins(23px, 14px, 21px, 0px);
editPrivacyPadding: margins(23px, 0px, 21px, 0px);
editPrivacyWarningPadding: margins(23px, 14px, 21px, 0px);
editPrivacyTitle: FlatLabel(defaultFlatLabel) {
width: 320px;
minWidth: 320px;
textFg: boxTitleFg;
maxHeight: 56px;
style: TextStyle(defaultTextStyle) {
@ -612,7 +612,7 @@ editPrivacyTitle: FlatLabel(defaultFlatLabel) {
}
editPrivacyTitlePadding: margins(23px, 20px, 21px, 13px);
editPrivacyLabel: FlatLabel(defaultFlatLabel) {
width: 320px;
minWidth: 320px;
textFg: membersAboutLimitFg;
style: defaultTextStyle;
}
@ -624,13 +624,13 @@ changePhoneIcon: icon {
{ "phone_simcard_to", changePhoneSimcardTo, point(78px, 0px) }
};
changePhoneDescription: FlatLabel(boxLabel) {
width: 332px;
minWidth: 332px;
align: align(top);
}
changePhoneIconTop: 20px;
changePhoneDescriptionTop: 96px;
changePhoneLabel: FlatLabel(defaultFlatLabel) {
width: 275px;
minWidth: 275px;
textFg: windowSubTextFg;
}
changePhoneError: FlatLabel(changePhoneLabel) {
@ -695,7 +695,7 @@ mutePhotoButton: PeerAvatarButton {
photoSize: 40px;
}
muteChatTitle: FlatLabel(boxLabel) {
width: 235px;
minWidth: 235px;
maxHeight: 20px; // block word wrap
style: TextStyle(boxTextStyle) {
font: font(boxFontSize semibold);

View File

@ -0,0 +1,970 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/peers/edit_peer_info_box.h"
#include <rpl/range.h>
#include <rpl/flatten_latest.h>
#include "info/profile/info_profile_button.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/toast/toast.h"
#include "ui/special_buttons.h"
#include "boxes/confirm_box.h"
#include "boxes/photo_crop_box.h"
#include "boxes/add_contact_box.h"
#include "mtproto/sender.h"
#include "lang/lang_keys.h"
#include "core/file_utilities.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "application.h"
#include "auth_session.h"
#include "observer_peer.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
namespace {
constexpr auto kUsernameCheckTimeout = TimeMs(200);
class Controller
: private MTP::Sender
, private base::enable_weak_from_this {
public:
Controller(
not_null<BoxContent*> box,
not_null<ChannelData*> channel);
object_ptr<Ui::VerticalLayout> createContent();
void setFocus();
private:
enum class Privacy {
Public,
Private,
};
enum class Invites {
Everyone,
OnlyAdmins,
};
enum class UsernameState {
Normal,
TooMany,
NotAvailable,
};
struct Controls {
Ui::InputField *title = nullptr;
Ui::InputArea *description = nullptr;
Ui::NewAvatarButton *photo = nullptr;
rpl::lifetime initialPhotoImageWaiting;
std::shared_ptr<Ui::RadioenumGroup<Privacy>> privacy;
Ui::SlideWrap<Ui::RpWidget> *usernameWrap = nullptr;
Ui::UsernameInput *username = nullptr;
base::unique_qptr<Ui::FlatLabel> usernameResult;
const style::FlatLabel *usernameResultStyle = nullptr;
Ui::SlideWrap<Ui::RpWidget> *createInviteLinkWrap = nullptr;
Ui::SlideWrap<Ui::RpWidget> *editInviteLinkWrap = nullptr;
Ui::FlatLabel *inviteLink = nullptr;
std::shared_ptr<Ui::RadioenumGroup<Invites>> invites;
Ui::Checkbox *signatures = nullptr;
};
base::lambda<QString()> computeTitle() const;
object_ptr<Ui::RpWidget> createPhotoAndTitleEdit();
object_ptr<Ui::RpWidget> createTitleEdit();
object_ptr<Ui::RpWidget> createPhotoEdit();
object_ptr<Ui::RpWidget> createDescriptionEdit();
object_ptr<Ui::RpWidget> createPrivaciesEdit();
object_ptr<Ui::RpWidget> createUsernameEdit();
object_ptr<Ui::RpWidget> createInviteLinkCreate();
object_ptr<Ui::RpWidget> createInviteLinkEdit();
object_ptr<Ui::RpWidget> createSignaturesEdit();
object_ptr<Ui::RpWidget> createInvitesEdit();
object_ptr<Ui::RpWidget> createDeleteButton();
void refreshInitialPhotoImage();
void submitTitle();
void submitDescription();
void save();
void deleteWithConfirmation();
void choosePhotoDelayed();
void choosePhoto();
void suggestPhotoFile(
const FileDialog::OpenResult &result);
void suggestPhoto(const QImage &image);
void privacyChanged(Privacy value);
void checkUsernameAvailability();
void askUsernameRevoke();
void usernameChanged();
void showUsernameError(rpl::producer<QString> &&error);
void showUsernameGood();
void showUsernameResult(
rpl::producer<QString> &&text,
not_null<const style::FlatLabel*> st);
bool canEditInviteLink() const;
bool inviteLinkShown() const;
void refreshEditInviteLink();
void refreshCreateInviteLink();
void createInviteLink();
void revokeInviteLink();
void exportInviteLink(const QString &confirmation);
not_null<BoxContent*> _box;
not_null<ChannelData*> _channel;
bool _isGroup;
base::unique_qptr<Ui::VerticalLayout> _wrap;
Controls _controls;
base::Timer _checkUsernameTimer;
mtpRequestId _checkUsernameRequestId = 0;
UsernameState _usernameState = UsernameState::Normal;
rpl::event_stream<rpl::producer<QString>> _usernameResultTexts;
};
Controller::Controller(
not_null<BoxContent*> box,
not_null<ChannelData*> channel)
: _box(box)
, _channel(channel)
, _isGroup(_channel->isMegagroup())
, _checkUsernameTimer([this] { checkUsernameAvailability(); }) {
_box->setTitle(computeTitle());
_box->addButton(langFactory(lng_settings_save), [this] {
save();
});
_box->addButton(langFactory(lng_cancel), [this] {
_box->closeBox();
});
}
base::lambda<QString()> Controller::computeTitle() const {
return langFactory(_isGroup
? lng_edit_group
: lng_edit_channel_title);
}
object_ptr<Ui::VerticalLayout> Controller::createContent() {
auto result = object_ptr<Ui::VerticalLayout>(_box);
_wrap.reset(result.data());
_controls = Controls();
_wrap->add(createPhotoAndTitleEdit());
_wrap->add(createDescriptionEdit());
_wrap->add(createPrivaciesEdit());
_wrap->add(createInviteLinkCreate());
_wrap->add(createInviteLinkEdit());
_wrap->add(createSignaturesEdit());
_wrap->add(createInvitesEdit());
_wrap->add(createDeleteButton());
_wrap->resizeToWidth(st::boxWideWidth);
return result;
}
void Controller::setFocus() {
if (_controls.title) {
_controls.title->setFocusFast();
}
}
object_ptr<Ui::RpWidget> Controller::createPhotoAndTitleEdit() {
Expects(_wrap != nullptr);
if (!_channel->canEditInformation()) {
return nullptr;
}
auto result = object_ptr<Ui::RpWidget>(_wrap);
auto container = result.data();
auto photoWrap = Ui::AttachParentChild(
container,
createPhotoEdit());
auto titleEdit = Ui::AttachParentChild(
container,
createTitleEdit());
photoWrap->heightValue()
| rpl::start_with_next([container](int height) {
container->resize(container->width(), height);
}, photoWrap->lifetime());
container->widthValue()
| rpl::start_with_next([titleEdit](int width) {
auto left = st::editPeerPhotoMargins.left()
+ st::editPeerPhotoSize;
titleEdit->resizeToWidth(width - left);
titleEdit->moveToLeft(left, 0, width);
}, titleEdit->lifetime());
return result;
}
object_ptr<Ui::RpWidget> Controller::createPhotoEdit() {
Expects(_wrap != nullptr);
using PhotoWrap = Ui::PaddingWrap<Ui::NewAvatarButton>;
auto photoWrap = object_ptr<PhotoWrap>(
_wrap,
object_ptr<Ui::NewAvatarButton>(
_wrap,
st::editPeerPhotoSize,
st::editPeerPhotoIconPosition),
st::editPeerPhotoMargins);
_controls.photo = photoWrap->entity();
_controls.photo->addClickHandler([this] { choosePhotoDelayed(); });
_controls.initialPhotoImageWaiting = base::ObservableViewer(
Auth().downloaderTaskFinished())
| rpl::start_with_next([=] {
refreshInitialPhotoImage();
});
refreshInitialPhotoImage();
return photoWrap;
}
void Controller::refreshInitialPhotoImage() {
if (auto image = _channel->currentUserpic()) {
image->load();
if (image->loaded()) {
_controls.photo->setImage(image->pixNoCache(
st::editPeerPhotoSize * cIntRetinaFactor(),
st::editPeerPhotoSize * cIntRetinaFactor(),
Images::Option::Smooth).toImage());
_controls.initialPhotoImageWaiting.destroy();
}
} else {
_controls.initialPhotoImageWaiting.destroy();
}
}
object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
Expects(_wrap != nullptr);
auto result = object_ptr<Ui::PaddingWrap<Ui::InputField>>(
_wrap,
object_ptr<Ui::InputField>(
_wrap,
st::defaultInputField,
langFactory(_isGroup
? lng_dlg_new_group_name
: lng_dlg_new_channel_name),
_channel->name),
st::editPeerTitleMargins);
QObject::connect(
result->entity(),
&Ui::InputField::submitted,
[this] { submitTitle(); });
_controls.title = result->entity();
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
Expects(_wrap != nullptr);
auto result = object_ptr<Ui::PaddingWrap<Ui::InputArea>>(
_wrap,
object_ptr<Ui::InputArea>(
_wrap,
st::editPeerDescription,
langFactory(lng_create_group_description),
_channel->about()),
st::editPeerDescriptionMargins);
QObject::connect(
result->entity(),
&Ui::InputArea::submitted,
[this] { submitDescription(); });
_controls.description = result->entity();
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createPrivaciesEdit() {
Expects(_wrap != nullptr);
if (!_channel->canEditUsername()) {
return nullptr;
}
auto result = object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>(
_wrap,
object_ptr<Ui::VerticalLayout>(_wrap),
st::editPeerPrivaciesMargins);
auto container = result->entity();
_controls.privacy = std::make_shared<Ui::RadioenumGroup<Privacy>>(
_channel->isPublic() ? Privacy::Public : Privacy::Private);
auto addButton = [&](
Privacy value,
LangKey groupTextKey,
LangKey channelTextKey,
LangKey groupAboutKey,
LangKey channelAboutKey) {
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::editPeerPrivacyTopSkip));
container->add(object_ptr<Ui::Radioenum<Privacy>>(
container,
_controls.privacy,
value,
lang(_isGroup ? groupTextKey : channelTextKey),
st::defaultBoxCheckbox));
container->add(object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
container,
object_ptr<Ui::FlatLabel>(
container,
Lang::Viewer(_isGroup ? groupAboutKey : channelAboutKey),
st::editPeerPrivacyLabel),
st::editPeerPrivacyLabelMargins));
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::editPeerPrivacyBottomSkip));
};
addButton(
Privacy::Public,
lng_create_public_group_title,
lng_create_public_channel_title,
lng_create_public_group_about,
lng_create_public_channel_about);
addButton(
Privacy::Private,
lng_create_private_group_title,
lng_create_private_channel_title,
lng_create_private_group_about,
lng_create_private_channel_about);
container->add(createUsernameEdit());
_controls.privacy->setChangedCallback([this](Privacy value) {
privacyChanged(value);
});
if (!_channel->isPublic()) {
checkUsernameAvailability();
}
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_wrap,
object_ptr<Ui::VerticalLayout>(_wrap),
st::editPeerUsernameMargins);
_controls.usernameWrap = result.data();
auto container = result->entity();
container->add(object_ptr<Ui::FlatLabel>(
container,
Lang::Viewer(lng_create_group_link),
st::editPeerSectionLabel));
auto placeholder = container->add(object_ptr<Ui::RpWidget>(
container));
placeholder->setAttribute(Qt::WA_TransparentForMouseEvents);
_controls.username = Ui::AttachParentChild(
container,
object_ptr<Ui::UsernameInput>(
container,
st::setupChannelLink,
base::lambda<QString()>(),
_channel->username,
true));
_controls.username->heightValue()
| rpl::start_with_next([placeholder](int height) {
placeholder->resize(placeholder->width(), height);
}, placeholder->lifetime());
placeholder->widthValue()
| rpl::start_with_next([this](int width) {
_controls.username->resize(
width,
_controls.username->height());
}, placeholder->lifetime());
_controls.username->move(placeholder->pos());
QObject::connect(
_controls.username,
&Ui::UsernameInput::changed,
[this] { usernameChanged(); });
auto shown = (_controls.privacy->value() == Privacy::Public);
result->toggle(shown, anim::type::instant);
return std::move(result);
}
void Controller::privacyChanged(Privacy value) {
auto toggleEditUsername = [&] {
_controls.usernameWrap->toggle(
(value == Privacy::Public),
anim::type::instant);
};
auto refreshVisibilities = [&] {
// First we need to show everything, then hide anything.
// Otherwise the scroll position could jump up undesirably.
if (value == Privacy::Public) {
toggleEditUsername();
}
refreshCreateInviteLink();
refreshEditInviteLink();
if (value == Privacy::Public) {
_controls.usernameResult = nullptr;
checkUsernameAvailability();
} else {
toggleEditUsername();
}
};
if (value == Privacy::Public) {
if (_usernameState == UsernameState::TooMany) {
askUsernameRevoke();
return;
} else if (_usernameState == UsernameState::NotAvailable) {
_controls.privacy->setValue(Privacy::Private);
return;
}
refreshVisibilities();
_controls.username->setDisplayFocused(true);
_controls.username->setFocus();
} else {
request(base::take(_checkUsernameRequestId)).cancel();
_checkUsernameTimer.cancel();
refreshVisibilities();
setFocus();
}
}
void Controller::checkUsernameAvailability() {
if (!_controls.username) {
return;
}
auto initial = (_controls.privacy->value() != Privacy::Public);
auto checking = initial
? qsl(".bad.")
: _controls.username->getLastText().trimmed();
if (checking.size() < MinUsernameLength) {
return;
}
if (_checkUsernameRequestId) {
request(_checkUsernameRequestId).cancel();
}
_checkUsernameRequestId = request(MTPchannels_CheckUsername(
_channel->inputChannel,
MTP_string(checking)
)).done([=](const MTPBool &result) {
_checkUsernameRequestId = 0;
if (initial) {
return;
}
if (!mtpIsTrue(result) && checking != _channel->username) {
showUsernameError(
Lang::Viewer(lng_create_channel_link_occupied));
} else {
showUsernameGood();
}
}).fail([=](const RPCError &error) {
_checkUsernameRequestId = 0;
auto type = error.type();
_usernameState = UsernameState::Normal;
if (type == qstr("CHANNEL_PUBLIC_GROUP_NA")) {
_usernameState = UsernameState::NotAvailable;
_controls.privacy->setValue(Privacy::Private);
} else if (rand() % 10 < 2 || type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
_usernameState = UsernameState::TooMany;
if (_controls.privacy->value() == Privacy::Public) {
askUsernameRevoke();
}
} else if (initial) {
if (_controls.privacy->value() == Privacy::Public) {
_controls.usernameResult = nullptr;
_controls.username->setFocus();
}
} else if (type == qstr("USERNAME_INVALID")) {
showUsernameError(
Lang::Viewer(lng_create_channel_link_invalid));
} else if (type == qstr("USERNAME_OCCUPIED")
&& checking != _channel->username) {
showUsernameError(
Lang::Viewer(lng_create_channel_link_occupied));
}
}).send();
}
void Controller::askUsernameRevoke() {
_controls.privacy->setValue(Privacy::Private);
auto revokeCallback = base::lambda_guarded(this, [this] {
_usernameState = UsernameState::Normal;
_controls.privacy->setValue(Privacy::Public);
checkUsernameAvailability();
});
Ui::show(
Box<RevokePublicLinkBox>(std::move(revokeCallback)),
LayerOption::KeepOther);
}
void Controller::usernameChanged() {
auto username = _controls.username->getLastText().trimmed();
if (username.isEmpty()) {
_controls.usernameResult = nullptr;
_checkUsernameTimer.cancel();
return;
}
auto bad = base::find_if(username, [](QChar ch) {
return (ch < 'A' || ch > 'Z')
&& (ch < 'a' || ch > 'z')
&& (ch < '0' || ch > '9')
&& (ch != '_');
}) != username.end();
if (bad) {
showUsernameError(
Lang::Viewer(lng_create_channel_link_bad_symbols));
} else if (username.size() < MinUsernameLength) {
showUsernameError(
Lang::Viewer(lng_create_channel_link_too_short));
} else {
_controls.usernameResult = nullptr;
_checkUsernameTimer.callOnce(kUsernameCheckTimeout);
}
}
void Controller::showUsernameError(rpl::producer<QString> &&error) {
showUsernameResult(std::move(error), &st::editPeerUsernameError);
}
void Controller::showUsernameGood() {
showUsernameResult(
Lang::Viewer(lng_create_channel_link_available),
&st::editPeerUsernameGood);
}
void Controller::showUsernameResult(
rpl::producer<QString> &&text,
not_null<const style::FlatLabel*> st) {
if (!_controls.usernameResult
|| _controls.usernameResultStyle != st) {
_controls.usernameResultStyle = st;
_controls.usernameResult = base::make_unique_q<Ui::FlatLabel>(
_controls.usernameWrap,
_usernameResultTexts.events() | rpl::flatten_latest(),
*st);
auto label = _controls.usernameResult.get();
label->show();
label->widthValue()
| rpl::start_with_next([label] {
label->moveToRight(
st::editPeerUsernamePosition.x(),
st::editPeerUsernamePosition.y());
}, label->lifetime());
}
_usernameResultTexts.fire(std::move(text));
}
void Controller::createInviteLink() {
exportInviteLink(lang(_isGroup
? lng_group_invite_about
: lng_group_invite_about_channel));
}
void Controller::revokeInviteLink() {
exportInviteLink(lang(lng_group_invite_about_new));
}
void Controller::exportInviteLink(const QString &confirmation) {
auto boxPointer = std::make_shared<QPointer<ConfirmBox>>();
auto callback = base::lambda_guarded(this, [=] {
if (auto strong = *boxPointer) {
strong->closeBox();
}
Auth().api().exportInviteLink(_channel);
});
auto box = Box<ConfirmBox>(
confirmation,
std::move(callback));
*boxPointer = Ui::show(std::move(box), LayerOption::KeepOther);
}
bool Controller::canEditInviteLink() const {
if (_channel->canEditUsername()) {
return true;
}
return (!_channel->isPublic() && _channel->canAddMembers());
}
bool Controller::inviteLinkShown() const {
return !_controls.privacy
|| (_controls.privacy->value() == Privacy::Private);
}
object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
Expects(_wrap != nullptr);
if (!canEditInviteLink()) {
return nullptr;
}
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_wrap,
object_ptr<Ui::VerticalLayout>(_wrap),
st::editPeerInviteLinkMargins);
_controls.editInviteLinkWrap = result.data();
auto container = result->entity();
container->add(object_ptr<Ui::FlatLabel>(
container,
Lang::Viewer(lng_profile_invite_link_section),
st::editPeerSectionLabel));
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::editPeerInviteLinkSkip));
_controls.inviteLink = container->add(object_ptr<Ui::FlatLabel>(
container,
st::editPeerInviteLink));
_controls.inviteLink->setSelectable(true);
_controls.inviteLink->setContextCopyText(QString());
_controls.inviteLink->setBreakEverywhere(true);
_controls.inviteLink->setClickHandlerHook([this](auto&&...) {
Application::clipboard()->setText(_channel->inviteLink());
Ui::Toast::Show(lang(lng_group_invite_copied));
return false;
});
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::editPeerInviteLinkSkip));
container->add(object_ptr<Ui::LinkButton>(
container,
lang(lng_group_invite_create_new),
st::editPeerInviteLinkButton)
)->addClickHandler([this] { revokeInviteLink(); });
Notify::PeerUpdateValue(
_channel,
Notify::PeerUpdate::Flag::InviteLinkChanged)
| rpl::start_with_next([this] {
refreshEditInviteLink();
}, _controls.editInviteLinkWrap->lifetime());
return std::move(result);
}
void Controller::refreshEditInviteLink() {
auto link = _channel->inviteLink();
auto text = TextWithEntities();
if (!link.isEmpty()) {
text.text = link;
auto remove = qstr("https://");
if (text.text.startsWith(remove)) {
text.text.remove(0, remove.size());
}
text.entities.push_back(EntityInText(
EntityInTextCustomUrl,
0,
text.text.size(),
link));
}
_controls.inviteLink->setMarkedText(text);
// Hack to expand FlatLabel width to naturalWidth again.
_controls.editInviteLinkWrap->resizeToWidth(st::boxWideWidth);
_controls.editInviteLinkWrap->toggle(
inviteLinkShown() && !link.isEmpty(),
anim::type::instant);
}
object_ptr<Ui::RpWidget> Controller::createInviteLinkCreate() {
Expects(_wrap != nullptr);
if (!canEditInviteLink()) {
return nullptr;
}
auto result = object_ptr<Ui::SlideWrap<Ui::LinkButton>>(
_wrap,
object_ptr<Ui::LinkButton>(
_wrap,
lang(lng_group_invite_create),
st::editPeerInviteLinkButton),
st::editPeerInviteLinkMargins);
result->entity()->addClickHandler([this] {
createInviteLink();
});
_controls.createInviteLinkWrap = result.data();
Notify::PeerUpdateValue(
_channel,
Notify::PeerUpdate::Flag::InviteLinkChanged)
| rpl::start_with_next([this] {
refreshCreateInviteLink();
}, _controls.createInviteLinkWrap->lifetime());
return std::move(result);
}
void Controller::refreshCreateInviteLink() {
auto link = _channel->inviteLink();
_controls.createInviteLinkWrap->toggle(
inviteLinkShown() && link.isEmpty(),
anim::type::instant);
}
object_ptr<Ui::RpWidget> Controller::createSignaturesEdit() {
Expects(_wrap != nullptr);
if (!_channel->canEditInformation()
|| _channel->isMegagroup()) {
return nullptr;
}
auto result = object_ptr<Ui::VerticalLayout>(_wrap);
auto container = result.data();
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::defaultBoxCheckbox.margin.top()));
_controls.signatures = container->add(
object_ptr<Ui::PaddingWrap<Ui::Checkbox>>(
container,
object_ptr<Ui::Checkbox>(
container,
lang(lng_edit_sign_messages),
_channel->addsSignature(),
st::defaultBoxCheckbox),
st::editPeerSignaturesMargins))->entity();
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::defaultBoxCheckbox.margin.bottom()));
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createInvitesEdit() {
Expects(_wrap != nullptr);
if (!_channel->canEditInformation()
|| !_channel->isMegagroup()) {
return nullptr;
}
auto result = object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>(
_wrap,
object_ptr<Ui::VerticalLayout>(_wrap),
st::editPeerInvitesMargins);
auto container = result->entity();
container->add(object_ptr<Ui::FlatLabel>(
container,
Lang::Viewer(lng_edit_group_who_invites),
st::editPeerSectionLabel));
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::editPeerInvitesTopSkip));
_controls.invites = std::make_shared<Ui::RadioenumGroup<Invites>>(
_channel->anyoneCanAddMembers()
? Invites::Everyone
: Invites::OnlyAdmins);
auto addButton = [&](
Invites value,
LangKey textKey) {
container->add(object_ptr<Ui::FixedHeightWidget>(
container,
st::editPeerInvitesSkip));
container->add(object_ptr<Ui::Radioenum<Invites>>(
container,
_controls.invites,
value,
lang(textKey),
st::defaultBoxCheckbox));
};
addButton(
Invites::Everyone,
lng_edit_group_invites_everybody);
addButton(
Invites::OnlyAdmins,
lng_edit_group_invites_only_admins);
return std::move(result);
}
object_ptr<Ui::RpWidget> Controller::createDeleteButton() {
Expects(_wrap != nullptr);
if (!_channel->canDelete()) {
return nullptr;
}
auto text = lang(_isGroup
? lng_profile_delete_group
: lng_profile_delete_channel);
auto result = object_ptr<Ui::PaddingWrap<Ui::LinkButton>>(
_wrap,
object_ptr<Ui::LinkButton>(
_wrap,
text,
st::editPeerDeleteButton),
st::editPeerDeleteButtonMargins);
result->entity()->addClickHandler([this] {
deleteWithConfirmation();
});
return std::move(result);
}
void Controller::submitTitle() {
Expects(_controls.title != nullptr);
Expects(_controls.description != nullptr);
if (_controls.title->getLastText().isEmpty()) {
_controls.title->showError();
} else {
_controls.description->setFocus();
}
}
void Controller::submitDescription() {
Expects(_controls.title != nullptr);
Expects(_controls.description != nullptr);
if (_controls.title->getLastText().isEmpty()) {
_controls.title->showError();
_controls.title->setFocus();
} else {
save();
}
}
void Controller::save() {
Expects(_wrap != nullptr);
}
void Controller::deleteWithConfirmation() {
auto text = lang(_isGroup
? lng_sure_delete_group
: lng_sure_delete_channel);
auto deleteCallback = [channel = _channel] {
Ui::hideLayer();
Ui::showChatsList();
if (auto chat = channel->migrateFrom()) {
App::main()->deleteAndExit(chat);
}
MTP::send(
MTPchannels_DeleteChannel(channel->inputChannel),
App::main()->rpcDone(&MainWidget::sentUpdatesReceived),
App::main()->rpcFail(&MainWidget::deleteChannelFailed));
};
Ui::show(Box<ConfirmBox>(
text,
lang(lng_box_delete),
st::attentionBoxButton,
std::move(deleteCallback)), LayerOption::KeepOther);
}
void Controller::choosePhotoDelayed() {
App::CallDelayed(
st::defaultRippleAnimation.hideDuration,
this,
[this] { choosePhoto(); });
}
void Controller::choosePhoto() {
auto handleChosenPhoto = base::lambda_guarded(
_controls.photo,
[this](auto &&result) { suggestPhotoFile(result); });
auto imgExtensions = cImgExtensions();
auto filter = qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + FileDialog::AllFilesFilter();
FileDialog::GetOpenPath(
lang(lng_choose_image),
filter,
std::move(handleChosenPhoto));
}
void Controller::suggestPhotoFile(
const FileDialog::OpenResult &result) {
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
return;
}
auto image = [&] {
if (!result.remoteContent.isEmpty()) {
return App::readImage(result.remoteContent);
} else if (!result.paths.isEmpty()) {
return App::readImage(result.paths.front());
}
return QImage();
}();
suggestPhoto(image);
}
void Controller::suggestPhoto(const QImage &image) {
auto badAspect = [](int a, int b) {
return (a >= 10 * b);
};
if (image.isNull()
|| badAspect(image.width(), image.height())
|| badAspect(image.height(), image.width())) {
Ui::show(Box<InformBox>(lang(lng_bad_photo)));
return;
}
auto callback = [this](const QImage &cropped) {
_controls.photo->setImage(cropped);
};
auto box = Ui::show(
Box<PhotoCropBox>(image, _channel),
LayerOption::KeepOther);
QObject::connect(
box,
&PhotoCropBox::ready,
base::lambda_guarded(_controls.photo, std::move(callback)));
}
} // namespace
EditPeerInfoBox::EditPeerInfoBox(
QWidget*,
not_null<ChannelData*> channel)
: _channel(channel) {
}
void EditPeerInfoBox::prepare() {
auto controller = std::make_unique<Controller>(this, _channel);
_focusRequests.events()
| rpl::start_with_next(
[c = controller.get()] { c->setFocus(); },
lifetime());
auto content = controller->createContent();
content->heightValue()
| rpl::start_with_next([this](int height) {
setDimensions(st::boxWideWidth, height);
}, content->lifetime());
setInnerWidget(object_ptr<Ui::IgnoreMargins>(
this,
std::move(content)));
Ui::AttachAsChild(this, std::move(controller));
}

View File

@ -0,0 +1,41 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <rpl/event_stream.h>
#include "boxes/peers/manage_peer_box.h"
class EditPeerInfoBox : public BoxContent {
public:
EditPeerInfoBox(QWidget*, not_null<ChannelData*> channel);
void setInnerFocus() override {
_focusRequests.fire({});
}
protected:
void prepare() override;
private:
not_null<ChannelData*> _channel;
rpl::event_stream<> _focusRequests;
};

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/combine.h>
#include "lang/lang_keys.h"
#include "boxes/peers/edit_peer_info_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/labels.h"
#include "history/history_admin_log_section.h"
@ -35,7 +36,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace {
base::lambda<QString()> ManageBoxTitle(
base::lambda<QString()> ManagePeerTitle(
not_null<ChannelData*> channel) {
return langFactory(channel->isMegagroup()
? lng_manage_group_title
@ -83,6 +84,7 @@ void AddButtonWithCount(
button,
std::move(count),
st::managePeerButtonLabel);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
rpl::combine(button->widthValue(), label->widthValue())
| rpl::start_with_next([label](int outerWidth, int width) {
label->moveToRight(
@ -118,9 +120,7 @@ void FillManageBox(
Lang::Viewer(isGroup
? lng_manage_group_info
: lng_manage_channel_info),
[=] {
},
[=] { Ui::show(Box<EditPeerInfoBox>(channel)); },
st::infoIconInformation);
}
if (HasRecentActions(channel)) {
@ -195,10 +195,14 @@ ManagePeerBox::ManagePeerBox(
}
bool ManagePeerBox::Available(not_null<ChannelData*> channel) {
// canViewMembers() is removed, because in supergroups you
// see them in profile and in channels only admins can see them.
// canViewAdmins() is removed, because in supergroups it is
// always true and in channels it is equal to canViewBanned().
return channel->canViewMembers()
return false
// || channel->canViewMembers()
// || channel->canViewAdmins()
|| channel->canViewBanned()
|| channel->canEditInformation()
@ -208,7 +212,7 @@ bool ManagePeerBox::Available(not_null<ChannelData*> channel) {
void ManagePeerBox::prepare() {
_channel->updateFull();
setTitle(ManageBoxTitle(_channel));
setTitle(ManagePeerTitle(_channel));
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setupContent();

View File

@ -95,7 +95,7 @@ callMuteRight: 8px;
callNameTop: 15px;
callName: FlatLabel(defaultFlatLabel) {
width: 260px;
minWidth: 260px;
maxHeight: 30px;
textFg: callNameFg;
align: align(top);
@ -107,7 +107,7 @@ callName: FlatLabel(defaultFlatLabel) {
}
callStatusTop: 46px;
callStatus: FlatLabel(defaultFlatLabel) {
width: 260px;
minWidth: 260px;
maxHeight: 20px;
textFg: callStatusFg;
align: align(top);

View File

@ -29,7 +29,7 @@ switchPmButton: RoundButton(defaultBoxButton) {
textTop: 7px;
}
stickersRestrictedLabel: FlatLabel(defaultFlatLabel) {
width: 320px;
minWidth: 320px;
align: align(center);
textFg: noContactsColor;
}

View File

@ -260,7 +260,6 @@ infoProfileStatusLeft: 109px;
infoProfileStatusRight: 20px;
infoProfileStatusTop: 58px;
infoProfileStatusLabel: FlatLabel(defaultFlatLabel) {
width: 0px;
maxHeight: 18px;
textFg: windowSubTextFg;
style: TextStyle(defaultTextStyle) {
@ -324,7 +323,6 @@ infoSharedMediaButtonIconPosition: point(20px, 3px);
infoIconPosition: point(20px, 15px);
infoLabeledOneLine: FlatLabel(defaultFlatLabel) {
width: 0px; // No need to set minWidth in one-line text.
maxHeight: 20px;
style: TextStyle(defaultTextStyle) {
lineHeight: 19px;
@ -335,7 +333,7 @@ infoLabel: FlatLabel(infoLabeledOneLine) {
textFg: windowSubTextFg;
}
infoLabeled: FlatLabel(infoLabeledOneLine) {
width: 180px;
minWidth: 180px;
maxHeight: 0px;
margin: margins(5px, 5px, 5px, 5px);
}
@ -538,11 +536,58 @@ infoCommonGroupsList: PeerList(infoMembersList) {
}
managePeerButton: InfoProfileButton(infoProfileButton) {
padding: margins(76px, 10px, 76px, 8px);
padding: margins(76px, 12px, 76px, 10px);
}
managePeerButtonIconPosition: infoSharedMediaButtonIconPosition;
managePeerButtonIconPosition: point(20px, 5px);
managePeerButtonLabel: FlatLabel(defaultFlatLabel) {
textFg: windowActiveTextFg;
style: semiboldTextStyle;
}
managePeerButtonLabelPosition: point(25px, 10px);
editPeerDeleteButtonMargins: margins(23px, 16px, 23px, 16px);
editPeerDeleteButton: sessionTerminateAllButton;
editPeerPhotoSize: newGroupPhotoSize;
editPeerPhotoIconPosition: newGroupPhotoIconPosition;
editPeerPhotoMargins: margins(23px, 16px, 23px, 8px);
editPeerTitle: defaultInputField;
editPeerTitleMargins: margins(27px, 21px, 23px, 8px);
editPeerDescription: newGroupDescription;
editPeerDescriptionMargins: margins(23px, 5px, 23px, 16px);
editPeerPrivaciesMargins: margins(23px, 10px, 23px, 0px);
editPeerPrivacyTopSkip: 10px;
editPeerPrivacyBottomSkip: 16px;
editPeerPrivacyLabel: FlatLabel(defaultFlatLabel) {
minWidth: 263px;
textFg: windowSubTextFg;
}
editPeerPrivacyLabelMargins: margins(34px, 0px, 0px, 0px);
editPeerSectionLabel: FlatLabel(boxTitle) {
style: TextStyle(defaultTextStyle) {
font: font(15px semibold);
linkFont: font(15px semibold);
linkFontOver: font(15px semibold underline);
}
}
editPeerUsername: setupChannelLink;
editPeerUsernameSkip: 8px;
editPeerInviteLink: FlatLabel(defaultFlatLabel) {
minWidth: 1px; // for break everywhere
style: boxTextStyle;
}
editPeerInviteLinkButton: boxLinkButton;
editPeerUsernameMargins: margins(0px, 10px, 0px, 0px);
editPeerUsernameGood: FlatLabel(defaultFlatLabel) {
textFg: boxTextFgGood;
style: boxTextStyle;
}
editPeerUsernameError: FlatLabel(editPeerUsernameGood) {
textFg: boxTextFgError;
}
editPeerUsernamePosition: point(0px, 10px);
editPeerInviteLinkSkip: 10px;
editPeerInviteLinkMargins: margins(23px, 10px, 14px, 0px);
editPeerSignaturesMargins: margins(23px, 16px, 23px, 8px);
editPeerInvitesMargins: margins(23px, 18px, 23px, 16px);
editPeerInvitesTopSkip: 10px;
editPeerInvitesSkip: 10px;

View File

@ -235,8 +235,8 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupMuteToggle() {
NotificationsEnabledValue(peer)
)->addClickHandler([=] {
App::main()->updateNotifySetting(
_peer,
_peer->isMuted()
peer,
peer->isMuted()
? NotifySettingSetNotify
: NotifySettingSetMuted);
});

View File

@ -37,7 +37,7 @@ profileNameLeft: 26px;
profileNameTop: 9px;
profileNameLabel: FlatLabel(defaultFlatLabel) {
margin: margins(10px, 5px, 10px, 5px);
width: 160px;
minWidth: 160px;
maxHeight: 24px;
textFg: windowBoldFg;
style: TextStyle(defaultTextStyle) {
@ -98,15 +98,15 @@ profileBlockLabel: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
}
profileBlockTextPart: FlatLabel(defaultFlatLabel) {
width: 180px;
minWidth: 180px;
margin: margins(5px, 5px, 5px, 5px);
}
profileBlockOneLineTextPart: FlatLabel(profileBlockTextPart) {
width: 0px; // No need to set minWidth in one-line text.
minWidth: 0px; // No need to set minWidth in one-line text.
maxHeight: 20px;
}
profileBioLabel: FlatLabel(profileBlockOneLineTextPart) {
width: 120px;
minWidth: 120px;
maxHeight: 0px;
}
profileBlockOneLineSkip: 9px;
@ -116,7 +116,7 @@ profileEnableNotificationsTop: 7px;
profileSettingsBlockSkip: 8px;
profileInviteLinkText: FlatLabel(profileBlockTextPart) {
width: 1px; // Required for BreakEverywhere
minWidth: 1px; // Required for BreakEverywhere
}
profileLimitReachedSkip: 6px;
@ -135,7 +135,7 @@ profileMemberCreatorIconOver: icon {{ "profile_admin_star", profileAdminStarFgOv
profileMemberAdminIcon: icon {{ "profile_admin_star", profileOtherAdminStarFg, point(4px, 3px) }};
profileMemberAdminIconOver: icon {{ "profile_admin_star", profileOtherAdminStarFgOver, point(4px, 3px) }};
profileLimitReachedLabel: FlatLabel(defaultFlatLabel) {
width: 180px;
minWidth: 180px;
margin: margins(profileMemberPaddingLeft, 9px, profileMemberPaddingLeft, 6px);
style: TextStyle(defaultTextStyle) {
lineHeight: 19px;

View File

@ -315,8 +315,10 @@ void ParticipantsBoxController::restoreState(
loadMoreRows();
}
PeerListController::restoreState(std::move(state));
if (!_offset) {
setDescriptionText(QString());
if (delegate()->peerListFullRowsCount() > 0) {
setNonEmptyDescription();
} else if (_allLoaded) {
setDescriptionText(lang(lng_blocked_list_not_found));
}
sortByOnline();
}
@ -442,11 +444,9 @@ void ParticipantsBoxController::loadMoreRows() {
_loadRequestId = request(MTPchannels_GetParticipants(_channel->inputChannel, filter(), MTP_int(_offset), MTP_int(perPage))).done([this](const MTPchannels_ChannelParticipants &result) {
Expects(result.type() == mtpc_channels_channelParticipants);
auto firstLoad = !_offset;
_loadRequestId = 0;
if (!_offset) {
setDescriptionText((_role == Role::Restricted) ? lang(lng_group_blocked_list_about) : QString());
}
auto &participants = result.c_channels_channelParticipants();
App::feedUsers(participants.vusers);
@ -462,13 +462,26 @@ void ParticipantsBoxController::loadMoreRows() {
});
}
}
sortByOnline();
if (delegate()->peerListFullRowsCount() > 0) {
sortByOnline();
if (firstLoad) {
setNonEmptyDescription();
}
} else if (_allLoaded) {
setDescriptionText(lang(lng_blocked_list_not_found));
}
delegate()->peerListRefreshRows();
}).fail([this](const RPCError &error) {
_loadRequestId = 0;
}).send();
}
void ParticipantsBoxController::setNonEmptyDescription() {
setDescriptionText((_role == Role::Kicked)
? lang(lng_group_blocked_list_about)
: QString());
}
bool ParticipantsBoxController::feedMegagroupLastParticipants() {
if ((_role != Role::Members && _role != Role::Profile)
|| _offset > 0) {
@ -822,7 +835,8 @@ bool ParticipantsBoxController::removeRow(not_null<UserData*> user) {
} else {
delegate()->peerListRemoveRow(row);
}
if (!delegate()->peerListFullRowsCount()) {
if (_role != Role::Kicked
&& !delegate()->peerListFullRowsCount()) {
setDescriptionText(lang(lng_blocked_list_not_found));
}
return true;

View File

@ -120,6 +120,7 @@ private:
Role role,
not_null<Additional*> additional);
void setNonEmptyDescription();
void setupSortByOnline();
void setupListChangeViewers();
void sortByOnlineDelayed();

View File

@ -49,7 +49,7 @@ settingsNameLeft: 26px;
settingsNameTop: 9px;
settingsNameLabel: FlatLabel(defaultFlatLabel) {
margin: margins(10px, 5px, 10px, 5px);
width: 160px;
minWidth: 160px;
maxHeight: 24px;
textFg: windowBoldFg;
style: TextStyle(defaultTextStyle) {
@ -98,12 +98,12 @@ settingsBlockLabel: FlatLabel(settingsPrimaryLabel) {
textFg: windowSubTextFg;
}
settingsBlockOneLineTextPart: FlatLabel(settingsPrimaryLabel) {
width: 0px; // No need to set minWidth in one-line text.
minWidth: 0px; // No need to set minWidth in one-line text.
margin: margins(5px, 5px, 5px, 5px);
maxHeight: 20px;
}
settingsBioValue: FlatLabel(settingsBlockOneLineTextPart) {
width: 120px;
minWidth: 120px;
maxHeight: 0px;
}
settingsSubSkip: 4px;

View File

@ -168,6 +168,10 @@ public:
void setImage(const QImage &image);
int naturalWidth() const override {
return height();
}
protected:
void paintEvent(QPaintEvent *e) override;

View File

@ -1744,7 +1744,12 @@ void FlatInput::onTextChange(const QString &text) {
if (App::wnd()) App::wnd()->updateGlobalMenu();
}
InputArea::InputArea(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory, const QString &val) : TWidget(parent)
InputArea::InputArea(
QWidget *parent,
const style::InputField &st,
base::lambda<QString()> placeholderFactory,
const QString &val)
: RpWidget(parent)
, _st(st)
, _inner(this)
, _oldtext(val)
@ -3272,7 +3277,12 @@ void InputField::setErrorShown(bool error) {
}
}
MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory, const QString &val) : TWidgetHelper<QLineEdit>(val, parent)
MaskedInputField::MaskedInputField(
QWidget *parent,
const style::InputField &st,
base::lambda<QString()> placeholderFactory,
const QString &val)
: Parent(val, parent)
, _st(st)
, _oldtext(val)
, _placeholderFactory(std::move(placeholderFactory)) {
@ -3350,15 +3360,18 @@ void MaskedInputField::onTouchTimer() {
_touchRightButton = true;
}
bool MaskedInputField::event(QEvent *e) {
if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
QTouchEvent *ev = static_cast<QTouchEvent*>(e);
if (ev->device()->type() == QTouchDevice::TouchScreen) {
touchEvent(ev);
return QLineEdit::event(e);
bool MaskedInputField::eventHook(QEvent *e) {
auto type = e->type();
if (type == QEvent::TouchBegin
|| type == QEvent::TouchUpdate
|| type == QEvent::TouchEnd
|| type == QEvent::TouchCancel) {
auto event = static_cast<QTouchEvent*>(e);
if (event->device()->type() == QTouchDevice::TouchScreen) {
touchEvent(event);
}
}
return QLineEdit::event(e);
return Parent::eventHook(e);
}
void MaskedInputField::touchEvent(QTouchEvent *e) {

View File

@ -324,11 +324,15 @@ enum class CtrlEnterSubmit {
Both,
};
class InputArea : public TWidget, private base::Subscriber {
class InputArea : public RpWidget, private base::Subscriber {
Q_OBJECT
public:
InputArea(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString());
InputArea(
QWidget *parent,
const style::InputField &st,
base::lambda<QString()> placeholderFactory = base::lambda<QString()>(),
const QString &val = QString());
void showError();
@ -687,9 +691,12 @@ private:
bool _correcting = false;
};
class MaskedInputField : public TWidgetHelper<QLineEdit>, private base::Subscriber {
class MaskedInputField
: public RpWidgetWrap<QLineEdit>
, private base::Subscriber {
Q_OBJECT
using Parent = RpWidgetWrap<QLineEdit>;
public:
MaskedInputField(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString());
@ -748,7 +755,7 @@ protected:
void startBorderAnimation();
void startPlaceholderAnimation();
bool event(QEvent *e) override;
bool eventHook(QEvent *e) override;
void touchEvent(QTouchEvent *e);
void paintEvent(QPaintEvent *e) override;
void focusInEvent(QFocusEvent *e) override;

View File

@ -139,7 +139,7 @@ void LabelSimple::paintEvent(QPaintEvent *e) {
FlatLabel::FlatLabel(QWidget *parent, const style::FlatLabel &st)
: RpWidget(parent)
, _text(st.width ? st.width : QFIXED_MAX)
, _text(st.minWidth ? st.minWidth : QFIXED_MAX)
, _st(st)
, _contextCopyText(lang(lng_context_copy_text)) {
init();
@ -151,7 +151,7 @@ FlatLabel::FlatLabel(
InitType initType,
const style::FlatLabel &st)
: RpWidget(parent)
, _text(st.width ? st.width : QFIXED_MAX)
, _text(st.minWidth ? st.minWidth : QFIXED_MAX)
, _st(st)
, _contextCopyText(lang(lng_context_copy_text)) {
if (initType == InitType::Rich) {
@ -167,7 +167,7 @@ FlatLabel::FlatLabel(
rpl::producer<QString> &&text,
const style::FlatLabel &st)
: RpWidget(parent)
, _text(st.width ? st.width : QFIXED_MAX)
, _text(st.minWidth ? st.minWidth : QFIXED_MAX)
, _st(st)
, _contextCopyText(lang(lng_context_copy_text)) {
textUpdated();
@ -182,7 +182,7 @@ FlatLabel::FlatLabel(
rpl::producer<TextWithEntities> &&text,
const style::FlatLabel &st)
: RpWidget(parent)
, _text(st.width ? st.width : QFIXED_MAX)
, _text(st.minWidth ? st.minWidth : QFIXED_MAX)
, _st(st)
, _contextCopyText(lang(lng_context_copy_text)) {
textUpdated();
@ -259,7 +259,7 @@ QMargins FlatLabel::getMargins() const {
int FlatLabel::countTextWidth() const {
return _allowedWidth
? _allowedWidth
: (_st.width ? _st.width : _text.maxWidth());
: (_st.minWidth ? _st.minWidth : _text.maxWidth());
}
int FlatLabel::countTextHeight(int textWidth) {

View File

@ -29,7 +29,7 @@ LabelSimple {
FlatLabel {
margin: margins;
width: pixels;
minWidth: pixels;
align: align;
textFg: color;
maxHeight: pixels;
@ -522,7 +522,7 @@ defaultLabelSimple: LabelSimple {
}
defaultFlatLabel: FlatLabel {
width: 0px;
minWidth: 0px;
maxHeight: 0px;
align: align(left);
textFg: windowFg;

View File

@ -158,4 +158,42 @@ public:
};
class IgnoreMargins : public Wrap<RpWidget> {
using Parent = Wrap<RpWidget>;
public:
IgnoreMargins(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());
}
}
QMargins getMargins() const override {
return QMargins();
}
protected:
int resizeGetHeight(int newWidth) override {
if (auto weak = wrapped()) {
weak->resizeToWidth(newWidth);
weak->moveToLeft(0, 0);
return weak->heightNoMargins();
}
return height();
}
private:
void wrappedSizeUpdated(QSize size) override {
auto margins = wrapped()->getMargins();
resize(
size.width() - margins.left() - margins.right(),
size.height() - margins.top() - margins.bottom());
}
};
} // namespace Ui

View File

@ -34,6 +34,8 @@
<(src_loc)/base/virtual_method.h
<(src_loc)/base/weak_unique_ptr.h
<(src_loc)/base/zlib_help.h
<(src_loc)/boxes/peers/edit_peer_info_box.cpp
<(src_loc)/boxes/peers/edit_peer_info_box.h
<(src_loc)/boxes/peers/manage_peer_box.cpp
<(src_loc)/boxes/peers/manage_peer_box.h
<(src_loc)/boxes/about_box.cpp