mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-30 15:30:20 +00:00
Add EditPeerInfoBox without saving.
This commit is contained in:
parent
3998fad7ef
commit
8ff0120642
@ -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.";
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
970
Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
Normal file
970
Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
Normal 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));
|
||||
}
|
41
Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h
Normal file
41
Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h
Normal 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;
|
||||
|
||||
};
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -29,7 +29,7 @@ switchPmButton: RoundButton(defaultBoxButton) {
|
||||
textTop: 7px;
|
||||
}
|
||||
stickersRestrictedLabel: FlatLabel(defaultFlatLabel) {
|
||||
width: 320px;
|
||||
minWidth: 320px;
|
||||
align: align(center);
|
||||
textFg: noContactsColor;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -235,8 +235,8 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupMuteToggle() {
|
||||
NotificationsEnabledValue(peer)
|
||||
)->addClickHandler([=] {
|
||||
App::main()->updateNotifySetting(
|
||||
_peer,
|
||||
_peer->isMuted()
|
||||
peer,
|
||||
peer->isMuted()
|
||||
? NotifySettingSetNotify
|
||||
: NotifySettingSetMuted);
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -120,6 +120,7 @@ private:
|
||||
Role role,
|
||||
not_null<Additional*> additional);
|
||||
|
||||
void setNonEmptyDescription();
|
||||
void setupSortByOnline();
|
||||
void setupListChangeViewers();
|
||||
void sortByOnlineDelayed();
|
||||
|
@ -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;
|
||||
|
@ -168,6 +168,10 @@ public:
|
||||
|
||||
void setImage(const QImage &image);
|
||||
|
||||
int naturalWidth() const override {
|
||||
return height();
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user