diff --git a/Telegram/Resources/icons/info_rights_lock.png b/Telegram/Resources/icons/info_rights_lock.png new file mode 100644 index 000000000..86edf0241 Binary files /dev/null and b/Telegram/Resources/icons/info_rights_lock.png differ diff --git a/Telegram/Resources/icons/info_rights_lock@2x.png b/Telegram/Resources/icons/info_rights_lock@2x.png new file mode 100644 index 000000000..4aac0da7b Binary files /dev/null and b/Telegram/Resources/icons/info_rights_lock@2x.png differ diff --git a/Telegram/Resources/icons/info_rights_lock@3x.png b/Telegram/Resources/icons/info_rights_lock@3x.png new file mode 100644 index 000000000..e3f8fb5d9 Binary files /dev/null and b/Telegram/Resources/icons/info_rights_lock@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index beeadffcf..8101513b3 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1489,7 +1489,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_rights_edit_admin_header" = "What can this admin do?"; "lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with the same (or more limited) permissions."; "lng_rights_about_add_admins_no" = "This admin will not be able to add new admins."; -"lng_rights_about_admin_cant_edit" = "You cannot edit rights of this admin."; + +"lng_rights_about_admin_cant_edit" = "You are not allowed to edit the rights of this admin."; +"lng_rights_about_restriction_cant_edit" = "You cannot change the restrictions for this user."; +"lng_rights_restriction_for_all" = "This option is disabled for all members in Group Permissions. You can either enable the permission for everyone or make this user an admin."; +"lng_rights_permission_for_all" = "This option is enabled for all members in Group Permissions."; +"lng_rights_permission_unavailable" = "This permission is not available in public groups."; +"lng_rights_permission_cant_edit" = "You cannot edit this permission."; "lng_rights_user_restrictions" = "User restrictions"; "lng_rights_user_restrictions_header" = "What can this member do?"; "lng_rights_default_restrictions_header" = "What can members of this group do?"; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 6fdf27868..439d71c21 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -692,6 +692,7 @@ rightsCheckbox: Checkbox(defaultBoxCheckbox) { rightsToggle: Toggle(defaultToggle) { toggledFg: windowBgActive; untoggledFg: attentionButtonFg; + lockIcon: icon {{ "info_rights_lock", windowBgActive }}; xsize: 8px; vsize: 5px; vshift: 1px; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index a0f881361..8bba3824a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -221,12 +221,26 @@ void EditAdminBox::prepare() { | (prepareRights.c_chatAdminRights().vflags.v & (filterByMyRights ? channel->adminRights() : ~Flag(0))); - const auto disabledFlags = canSave() - ? (disabledByDefaults - | ((!channel || channel->amCreator()) - ? Flags(0) - : ~channel->adminRights())) - : ~Flags(0); + const auto disabledMessages = [&] { + auto result = std::map(); + if (!canSave()) { + result.emplace( + ~Flags(0), + lang(lng_rights_about_admin_cant_edit)); + } else { + result.emplace( + disabledByDefaults, + lang(lng_rights_permission_for_all)); + if (const auto channel = peer()->asChannel()) { + if (!channel->amCreator()) { + result.emplace( + ~channel->adminRights(), + lang(lng_rights_permission_cant_edit)); + } + } + } + return result; + }(); const auto anyoneCanAddMembers = chat ? chat->anyoneCanAddMembers() @@ -235,7 +249,7 @@ void EditAdminBox::prepare() { this, lng_rights_edit_admin_header, prepareFlags, - disabledFlags, + disabledMessages, peer()->isChat() || peer()->isMegagroup(), anyoneCanAddMembers); addControl(std::move(checkboxes), QMargins()); @@ -316,18 +330,29 @@ void EditRestrictedBox::prepare() { | ((channel && channel->isPublic()) ? (Flag::f_change_info | Flag::f_pin_messages) : Flags(0)); - const auto disabledFlags = canSave() - ? (defaultRestrictions - | ((channel && channel->isPublic()) - ? (Flag::f_change_info | Flag::f_pin_messages) - : Flags(0))) - : ~Flags(0); + const auto disabledMessages = [&] { + auto result = std::map(); + if (!canSave()) { + result.emplace( + ~Flags(0), + lang(lng_rights_about_restriction_cant_edit)); + } else { + const auto disabled = defaultRestrictions + | ((channel && channel->isPublic()) + ? (Flag::f_change_info | Flag::f_pin_messages) + : Flags(0)); + result.emplace( + disabled, + lang(lng_rights_restriction_for_all)); + } + return result; + }(); auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions( this, lng_rights_user_restrictions_header, prepareFlags, - disabledFlags); + disabledMessages); addControl(std::move(checkboxes), QMargins()); _until = prepareRights.c_chatBannedRights().vuntil_date.v; @@ -424,7 +449,7 @@ void EditRestrictedBox::createUntilVariants() { if (!canSave() && _untilGroup->value() != value) { return; } - _untilVariants.push_back(base::unique_qptr( + _untilVariants.emplace_back( addControl( object_ptr( this, @@ -432,7 +457,7 @@ void EditRestrictedBox::createUntilVariants() { value, text, st::defaultBoxCheckbox), - st::rightsToggleMargin))); + st::rightsToggleMargin)); if (!canSave()) { _untilVariants.back()->setDisabled(true); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index c3c87ebf9..adfcd3b21 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/vertical_layout.h" #include "ui/widgets/labels.h" #include "ui/widgets/checkbox.h" +#include "ui/toast/toast.h" #include "info/profile/info_profile_button.h" #include "info/profile/info_profile_icon.h" #include "info/profile/info_profile_values.h" @@ -280,24 +281,26 @@ void EditPeerPermissionsBox::prepare() { } Unexpected("User in EditPeerPermissionsBox."); }(); - const auto disabledFlags = [&] { - if (const auto chat = _peer->asChat()) { - return Flags(0) - | disabledByAdminRights; - } else if (const auto channel = _peer->asChannel()) { - return (channel->isPublic() - ? (Flag::f_change_info | Flag::f_pin_messages) - : Flags(0)) - | disabledByAdminRights; + const auto disabledMessages = [&] { + auto result = std::map(); + result.emplace( + disabledByAdminRights, + lang(lng_rights_permission_cant_edit)); + if (const auto channel = _peer->asChannel()) { + if (channel->isPublic()) { + result.emplace( + Flag::f_change_info | Flag::f_pin_messages, + lang(lng_rights_permission_unavailable)); + } } - Unexpected("User in EditPeerPermissionsBox."); + return result; }(); auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions( this, lng_rights_default_restrictions_header, restrictions, - disabledFlags); + disabledMessages); inner->add(std::move(checkboxes)); @@ -353,12 +356,15 @@ void EditPeerPermissionsBox::addBannedButtons( } } -template +template < + typename Flags, + typename DisabledMessagePairs, + typename FlagLabelPairs> EditFlagsControl CreateEditFlags( QWidget *parent, LangKey header, Flags checked, - Flags disabled, + const DisabledMessagePairs &disabledMessagePairs, const FlagLabelPairs &flagLabelPairs) { auto widget = object_ptr(parent); const auto container = widget.data(); @@ -394,24 +400,38 @@ EditFlagsControl CreateEditFlags( st::rightsHeaderMargin); auto addCheckbox = [&](Flags flags, const QString &text) { + const auto lockedIt = ranges::find_if( + disabledMessagePairs, + [&](const auto &pair) { return (pair.first & flags) != 0; }); + const auto locked = (lockedIt != end(disabledMessagePairs)) + ? std::make_optional(lockedIt->second) + : std::nullopt; + const auto toggled = ((checked & flags) != 0); + auto toggle = std::make_unique( + st::rightsToggle, + toggled); + toggle->setLocked(locked.has_value()); const auto control = container->add( object_ptr( container, text, - (checked & flags) != 0, st::rightsCheckbox, - st::rightsToggle), + std::move(toggle)), st::rightsToggleMargin); control->checkedChanges( ) | rpl::start_with_next([=](bool checked) { - InvokeQueued(control, [=] { - applyDependencies(control); - changes->fire({}); - }); + if (locked.has_value()) { + if (checked != toggled) { + Ui::Toast::Show(*locked); + control->setChecked(toggled); + } + } else { + InvokeQueued(control, [=] { + applyDependencies(control); + changes->fire({}); + }); + } }, control->lifetime()); - if ((disabled & flags) != 0) { - control->setDisabled(true); - } checkboxes->emplace(flags, control); }; for (const auto &[flags, label] : flagLabelPairs) { @@ -434,12 +454,12 @@ EditFlagsControl CreateEditRestrictions( QWidget *parent, LangKey header, MTPDchatBannedRights::Flags restrictions, - MTPDchatBannedRights::Flags disabled) { + std::map disabledMessages) { auto result = CreateEditFlags( parent, header, NegateRestrictions(restrictions), - disabled, + disabledMessages, RestrictionLabels()); result.value = [original = std::move(result.value)]{ return NegateRestrictions(original()); @@ -455,13 +475,13 @@ EditFlagsControl CreateEditAdminRights( QWidget *parent, LangKey header, MTPDchatAdminRights::Flags rights, - MTPDchatAdminRights::Flags disabled, + std::map disabledMessages, bool isGroup, bool anyoneCanAddMembers) { return CreateEditFlags( parent, header, rights, - disabled, + disabledMessages, AdminRightLabels(isGroup, anyoneCanAddMembers)); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h index f771bb38a..cd6998f83 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h @@ -46,13 +46,13 @@ EditFlagsControl CreateEditRestrictions( QWidget *parent, LangKey header, MTPDchatBannedRights::Flags restrictions, - MTPDchatBannedRights::Flags disabled); + std::map disabledMessages); EditFlagsControl CreateEditAdminRights( QWidget *parent, LangKey header, MTPDchatAdminRights::Flags rights, - MTPDchatAdminRights::Flags disabled, + std::map disabledMessages, bool isGroup, bool anyoneCanAddMembers); diff --git a/Telegram/SourceFiles/ui/twidget.cpp b/Telegram/SourceFiles/ui/twidget.cpp index 0184bdf5c..be9ecce12 100644 --- a/Telegram/SourceFiles/ui/twidget.cpp +++ b/Telegram/SourceFiles/ui/twidget.cpp @@ -22,7 +22,7 @@ bool ValidateFont(const QString &familyName, int flags = 0) { checkFont.setStyleStrategy(QFont::PreferQuality); auto realFamily = QFontInfo(checkFont).family(); if (realFamily.trimmed().compare(familyName, Qt::CaseInsensitive)) { - LOG(("Font Error: could not resolve '%1' font, got '%2' after feeding '%3'.").arg(familyName).arg(realFamily)); + LOG(("Font Error: could not resolve '%1' font, got '%2'.").arg(familyName).arg(realFamily)); return false; } diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.cpp b/Telegram/SourceFiles/ui/widgets/checkbox.cpp index da8d5dfc7..188de1791 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.cpp +++ b/Telegram/SourceFiles/ui/widgets/checkbox.cpp @@ -115,20 +115,28 @@ void ToggleView::paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) p.drawEllipse(fgRect); if (_st->xsize > 0) { - paintXV(p, toggleLeft, top, outerWidth, toggled, fgBrush); + p.setPen(Qt::NoPen); + p.setBrush(fgBrush); + if (_locked) { + const auto color = anim::color(_st->untoggledFg, _st->toggledFg, toggled); + _st->lockIcon.paint(p, toggleLeft, top, outerWidth, color); + } else { + paintXV(p, toggleLeft, top, outerWidth, toggled, fgBrush); + } } } void ToggleView::paintXV(Painter &p, int left, int top, int outerWidth, float64 toggled, const QBrush &brush) { - Assert(_st->vsize > 0); - Assert(_st->stroke > 0); + Expects(_st->vsize > 0); + Expects(_st->stroke > 0); + static const auto sqrt2 = sqrt(2.); - auto stroke = (0. + _st->stroke) / sqrt2; + const auto stroke = (0. + _st->stroke) / sqrt2; if (toggled < 1) { // Just X or X->V. - auto xSize = 0. + _st->xsize; - auto xLeft = left + (_st->diameter - xSize) / 2.; - auto xTop = top + (_st->diameter - xSize) / 2.; + const auto xSize = 0. + _st->xsize; + const auto xLeft = left + (_st->diameter - xSize) / 2.; + const auto xTop = top + (_st->diameter - xSize) / 2.; QPointF pathX[] = { { xLeft, xTop + stroke }, { xLeft + stroke, xTop }, @@ -148,10 +156,10 @@ void ToggleView::paintXV(Painter &p, int left, int top, int outerWidth, float64 } if (toggled > 0) { // X->V. - auto vSize = 0. + _st->vsize; - auto fSize = (xSize + vSize - 2. * stroke); - auto vLeft = left + (_st->diameter - fSize) / 2.; - auto vTop = 0. + xTop + _st->vshift; + const auto vSize = 0. + _st->vsize; + const auto fSize = (xSize + vSize - 2. * stroke); + const auto vLeft = left + (_st->diameter - fSize) / 2.; + const auto vTop = 0. + xTop + _st->vshift; QPointF pathV[] = { { vLeft, vTop + xSize - vSize + stroke }, { vLeft + stroke, vTop + xSize - vSize }, @@ -176,12 +184,12 @@ void ToggleView::paintXV(Painter &p, int left, int top, int outerWidth, float64 } } else { // Just V. - auto xSize = 0. + _st->xsize; - auto xTop = top + (_st->diameter - xSize) / 2.; - auto vSize = 0. + _st->vsize; - auto fSize = (xSize + vSize - 2. * stroke); - auto vLeft = left + (_st->diameter - (_st->xsize + _st->vsize - 2. * stroke)) / 2.; - auto vTop = 0. + xTop + _st->vshift; + const auto xSize = 0. + _st->xsize; + const auto xTop = top + (_st->diameter - xSize) / 2.; + const auto vSize = 0. + _st->vsize; + const auto fSize = (xSize + vSize - 2. * stroke); + const auto vLeft = left + (_st->diameter - (_st->xsize + _st->vsize - 2. * stroke)) / 2.; + const auto vTop = 0. + xTop + _st->vshift; QPointF pathV[] = { { vLeft, vTop + xSize - vSize + stroke }, { vLeft + stroke, vTop + xSize - vSize }, @@ -214,6 +222,13 @@ bool ToggleView::checkRippleStartPosition(QPoint position) const { return QRect(QPoint(0, 0), rippleSize()).contains(position); } +void ToggleView::setLocked(bool locked) { + if (_locked != locked) { + _locked = locked; + update(); + } +} + CheckView::CheckView(const style::Check &st, bool checked, Fn updateCallback) : AbstractCheckView(st.duration, checked, std::move(updateCallback)) , _st(&st) { } diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.h b/Telegram/SourceFiles/ui/widgets/checkbox.h index 0f2bf00c9..f428ff201 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.h +++ b/Telegram/SourceFiles/ui/widgets/checkbox.h @@ -126,12 +126,14 @@ public: void paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) override; QImage prepareRippleMask() const override; bool checkRippleStartPosition(QPoint position) const override; + void setLocked(bool locked); private: void paintXV(Painter &p, int left, int top, int outerWidth, float64 toggled, const QBrush &brush); QSize rippleSize() const; not_null _st; + bool _locked = false; }; diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index b9511979a..793ecae8a 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -115,6 +115,7 @@ Toggle { vsize: pixels; vshift: pixels; stroke: pixels; + lockIcon: icon; rippleAreaPadding: pixels; }