From f7543e326161f4d98fb6745fa1e87f45f20314f6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 10 Jul 2017 13:43:25 +0300 Subject: [PATCH] Improve EditAdmin/RestrictedBox design. --- Telegram/Resources/langs/lang.strings | 8 +- Telegram/SourceFiles/boxes/boxes.style | 22 ++ .../boxes/edit_participant_box.cpp | 228 +++++++++++++----- .../SourceFiles/boxes/edit_participant_box.h | 24 +- .../history/history_admin_log_filter.cpp | 2 +- 5 files changed, 218 insertions(+), 66 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 66ba37903..aa6119ae5 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1296,8 +1296,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_rights_chat_send_media" = "Send media"; "lng_rights_chat_send_stickers" = "Send stickers & GIFs"; "lng_rights_chat_send_links" = "Embed links"; -"lng_rights_chat_banned_until" = "Restricted until: {when}"; +"lng_rights_chat_banned_until_header" = "Restricted until"; "lng_rights_chat_banned_forever" = "Forever"; +"lng_rights_chat_banned_day#one" = "For {count} day"; +"lng_rights_chat_banned_day#other" = "For {count} days"; +"lng_rights_chat_banned_week#one" = "For {count} week"; +"lng_rights_chat_banned_week#other" = "For {count} weeks"; +"lng_rights_chat_banned_custom" = "Custom"; +"lng_rights_chat_banned_custom_date" = "Until {date}"; "lng_rights_chat_banned_block" = "Ban and remove from group"; "lng_restricted_send_message" = "The admins of this group restricted you from writing here."; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 025bd2d0b..5b86a78b8 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -658,3 +658,25 @@ rightsToggle: Toggle(defaultToggle) { stroke: 2px; duration: 120; } +rightsDividerHeight: 11px; +rightsHeaderMargin: margins(23px, 20px, 23px, 8px); +rightsToggleMargin: margins(23px, 8px, 23px, 8px); +rightsAboutMargin: margins(23px, 8px, 23px, 8px); +rightsPhotoSize: 60px; +rightsPhotoMargin: margins(20px, 0px, 15px, 18px); +rightsNameStyle: TextStyle(semiboldTextStyle) { + font: font(15px semibold); + linkFont: font(15px semibold); + linkFontOver: font(15px semibold underline); +} +rightsNameTop: 8px; +rightsStatusTop: 32px; +rightsHeaderLabel: FlatLabel(boxLabel) { + style: TextStyle(semiboldTextStyle) { + font: font(boxFontSize semibold); + linkFont: font(boxFontSize semibold); + linkFontOver: font(boxFontSize semibold underline); + } + textFg: windowActiveTextFg; +} +rightsUntilMargin: margins(0px, 8px, 0px, 0px); diff --git a/Telegram/SourceFiles/boxes/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/edit_participant_box.cpp index 3d0b57d22..7c105ba5e 100644 --- a/Telegram/SourceFiles/boxes/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_participant_box.cpp @@ -25,11 +25,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "styles/style_boxes.h" +#include "styles/style_profile.h" #include "boxes/calendar_box.h" namespace { constexpr auto kMaxRestrictDelayDays = 366; +constexpr auto kSecondsInDay = 24 * 60 * 60; +constexpr auto kSecondsInWeek = 7 * kSecondsInDay; template void ApplyDependencies(CheckboxesMap &checkboxes, DependenciesMap &dependencies, QPointer changed) { @@ -70,53 +73,93 @@ void ApplyDependencies(CheckboxesMap &checkboxes, DependenciesMap &dependencies, } // namespace -class EditParticipantBox::Inner : public TWidget { +class EditParticipantBox::Divider : public TWidget { public: - Inner(QWidget *parent, gsl::not_null channel, gsl::not_null user, bool hasAdminRights) : TWidget(parent) - , _channel(channel) - , _user(user) - , _hasAdminRights(hasAdminRights) { - } - - template - QPointer addControl(object_ptr row) { - row->setParent(this); - _rows.push_back(std::move(row)); - return static_cast(_rows.back().data()); + Divider(QWidget *parent) : TWidget(parent) { } protected: int resizeGetHeight(int newWidth) override; - void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; -private: - gsl::not_null _channel; - gsl::not_null _user; - bool _hasAdminRights = false; - std::vector> _rows; - }; -int EditParticipantBox::Inner::resizeGetHeight(int newWidth) { - auto newHeight = st::contactsPhotoSize + st::contactsPadding.bottom(); - auto rowWidth = newWidth - st::boxPadding.left() - st::boxPadding.right(); - for (auto &&row : _rows) { - row->resizeToNaturalWidth(rowWidth); - newHeight += row->heightNoMargins(); - } - if (!_rows.empty()) { - newHeight += (_rows.size() - 1) * st::boxLittleSkip; - } - return newHeight; +int EditParticipantBox::Divider::resizeGetHeight(int newWidth) { + return st::rightsDividerHeight; } -void EditParticipantBox::Inner::resizeEvent(QResizeEvent *e) { - auto top = st::contactsPhotoSize + st::contactsPadding.bottom(); - for (auto &&row : _rows) { - row->moveToLeft(st::boxPadding.left(), top); - top += row->heightNoMargins() + st::boxLittleSkip; +void EditParticipantBox::Divider::paintEvent(QPaintEvent *e) { + Painter p(this); + p.fillRect(e->rect(), st::contactsAboutBg); + auto dividerFillTop = myrtlrect(0, 0, width(), st::profileDividerTop.height()); + st::profileDividerTop.fill(p, dividerFillTop); + auto dividerFillBottom = myrtlrect(0, height() - st::profileDividerBottom.height(), width(), st::profileDividerBottom.height()); + st::profileDividerBottom.fill(p, dividerFillBottom); +} + +class EditParticipantBox::Inner : public TWidget { +public: + Inner(QWidget *parent, gsl::not_null channel, gsl::not_null user, bool hasAdminRights); + + template + QPointer addControl(object_ptr widget, QMargins margin) { + doAddControl(std::move(widget), margin); + return static_cast(_rows.back().widget.data()); } + + void removeControl(QPointer widget); + +protected: + int resizeGetHeight(int newWidth) override; + void paintEvent(QPaintEvent *e) override; + +private: + void doAddControl(object_ptr widget, QMargins margin); + + gsl::not_null _channel; + gsl::not_null _user; + Text _userName; + bool _hasAdminRights = false; + struct Control { + object_ptr widget; + QMargins margin; + }; + std::vector _rows; + +}; + +EditParticipantBox::Inner::Inner(QWidget *parent, gsl::not_null channel, gsl::not_null user, bool hasAdminRights) : TWidget(parent) +, _channel(channel) +, _user(user) +, _hasAdminRights(hasAdminRights) { + _userName.setText(st::rightsNameStyle, App::peerName(_user), _textNameOptions); +} + +void EditParticipantBox::Inner::removeControl(QPointer widget) { + auto row = std::find_if(_rows.begin(), _rows.end(), [widget](auto &&row) { + return (row.widget == widget); + }); + t_assert(row != _rows.end()); + row->widget.destroy(); + _rows.erase(row); +} + +void EditParticipantBox::Inner::doAddControl(object_ptr widget, QMargins margin) { + widget->setParent(this); + _rows.push_back({ std::move(widget), margin }); + _rows.back().widget->show(); +} + +int EditParticipantBox::Inner::resizeGetHeight(int newWidth) { + auto newHeight = st::rightsPhotoMargin.top() + st::rightsPhotoSize + st::rightsPhotoMargin.bottom(); + for (auto &&row : _rows) { + auto rowWidth = newWidth - row.margin.left() - row.margin.right(); + newHeight += row.margin.top(); + row.widget->resizeToNaturalWidth(rowWidth); + row.widget->moveToLeft(row.margin.left(), newHeight); + newHeight += row.widget->heightNoMargins() + row.margin.bottom(); + } + return newHeight; } void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) { @@ -124,12 +167,12 @@ void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) { p.fillRect(e->rect(), st::boxBg); - _user->paintUserpicLeft(p, st::boxPadding.left(), 0, width(), st::contactsPhotoSize); + _user->paintUserpicLeft(p, st::rightsPhotoMargin.left(), st::rightsPhotoMargin.top(), width(), st::rightsPhotoSize); p.setPen(st::contactsNameFg); - auto namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); - auto namew = width() - namex - st::contactsPadding.right(); - _user->nameText.drawLeftElided(p, namex, st::contactsNameTop, namew, width()); + auto namex = st::rightsPhotoMargin.left() + st::rightsPhotoSize + st::rightsPhotoMargin.right(); + auto namew = width() - namex - st::rightsPhotoMargin.right(); + _userName.drawLeftElided(p, namex, st::rightsPhotoMargin.top() + st::rightsNameTop, namew, width()); auto statusText = [this] { if (_user->botInfo) { auto seesAllMessages = (_user->botInfo->readsAllHistory || _hasAdminRights); @@ -139,7 +182,7 @@ void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) { }; p.setFont(st::contactsStatusFont); p.setPen(st::contactsStatusFg); - p.drawTextLeft(namex, st::contactsStatusTop, width(), statusText()); + p.drawTextLeft(namex, st::rightsPhotoMargin.top() + st::rightsStatusTop, width(), statusText()); } EditParticipantBox::EditParticipantBox(QWidget*, gsl::not_null channel, gsl::not_null user, bool hasAdminRights) : BoxContent() @@ -153,14 +196,19 @@ void EditParticipantBox::prepare() { } template -QPointer EditParticipantBox::addControl(object_ptr row) { +QPointer EditParticipantBox::addControl(object_ptr widget, QMargins margin) { Expects(_inner != nullptr); - return _inner->addControl(std::move(row)); + return _inner->addControl(std::move(widget), margin); +} + +void EditParticipantBox::removeControl(QPointer widget) { + Expects(_inner != nullptr); + return _inner->removeControl(widget); } void EditParticipantBox::resizeToContent() { _inner->resizeToWidth(st::boxWideWidth); - setDimensions(_inner->width(), _inner->height()); + setDimensions(_inner->width(), qMin(_inner->height(), st::boxMaxListHeight)); } EditAdminBox::EditAdminBox(QWidget*, gsl::not_null channel, gsl::not_null user, const MTPChannelAdminRights &rights, base::lambda callback) : EditParticipantBox(nullptr, channel, user, (rights.c_channelAdminRights().vflags.v != 0)) @@ -185,12 +233,13 @@ void EditAdminBox::prepare() { setTitle(langFactory(lng_rights_edit_admin)); - addControl(object_ptr(this, lang(lng_rights_edit_admin_header), Ui::FlatLabel::InitType::Simple, st::boxLabel)); + addControl(object_ptr(this), QMargins()); + addControl(object_ptr(this, lang(lng_rights_edit_admin_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin); auto prepareRights = (_oldRights.c_channelAdminRights().vflags.v ? _oldRights : DefaultRights(channel())); auto addCheckbox = [this, &prepareRights](Flags flags, const QString &text) { auto checked = (prepareRights.c_channelAdminRights().vflags.v & flags) != 0; - auto control = addControl(object_ptr(this, text, checked, st::rightsCheckbox, st::rightsToggle)); + auto control = addControl(object_ptr(this, text, checked, st::rightsCheckbox, st::rightsToggle), st::rightsToggleMargin); subscribe(control->checkedChanged, [this, control](bool checked) { InvokeQueued(this, [this, control] { applyDependencies(control); }); }); @@ -219,7 +268,7 @@ void EditAdminBox::prepare() { auto addAdmins = _checkboxes.find(Flag::f_add_admins); if (addAdmins != _checkboxes.end()) { - _aboutAddAdmins = addControl(object_ptr(this, st::boxLabel)); + _aboutAddAdmins = addControl(object_ptr(this, st::boxLabel), st::rightsAboutMargin); t_assert(addAdmins != _checkboxes.end()); subscribe(addAdmins->second->checkedChanged, [this](bool checked) { refreshAboutAddAdminsText(); @@ -290,14 +339,15 @@ void EditRestrictedBox::prepare() { setTitle(langFactory(lng_rights_user_restrictions)); - addControl(object_ptr(this, lang(lng_rights_user_restrictions_header), Ui::FlatLabel::InitType::Simple, st::boxLabel)); + addControl(object_ptr(this), QMargins()); + addControl(object_ptr(this, lang(lng_rights_user_restrictions_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin); auto prepareRights = (_oldRights.c_channelBannedRights().vflags.v ? _oldRights : DefaultRights(channel())); _until = prepareRights.c_channelBannedRights().vuntil_date.v; auto addCheckbox = [this, &prepareRights](Flags flags, const QString &text) { auto checked = (prepareRights.c_channelBannedRights().vflags.v & flags) == 0; - auto control = addControl(object_ptr(this, text, checked, st::rightsCheckbox, st::rightsToggle)); + auto control = addControl(object_ptr(this, text, checked, st::rightsCheckbox, st::rightsToggle), st::rightsToggleMargin); subscribe(control->checkedChanged, [this, control](bool checked) { InvokeQueued(this, [this, control] { applyDependencies(control); }); }); @@ -309,8 +359,8 @@ void EditRestrictedBox::prepare() { addCheckbox(Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline, lang(lng_rights_chat_send_stickers)); addCheckbox(Flag::f_embed_links, lang(lng_rights_chat_send_links)); - _restrictUntil = addControl(object_ptr(this, QString(), st::boxLinkButton)); - _restrictUntil->setClickedCallback([this] { showRestrictUntil(); }); + addControl(object_ptr(this), st::rightsUntilMargin); + addControl(object_ptr(this, lang(lng_rights_chat_banned_until_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin); setRestrictUntil(_until); //addControl(object_ptr(this, lang(lng_rights_chat_banned_block), st::boxLinkButton)); @@ -327,7 +377,7 @@ void EditRestrictedBox::prepare() { newFlags |= checkbox.first; } } - _saveCallback(_oldRights, MTP_channelBannedRights(MTP_flags(newFlags), MTP_int(_until))); + _saveCallback(_oldRights, MTP_channelBannedRights(MTP_flags(newFlags), MTP_int(getRealUntilValue()))); }); addButton(langFactory(lng_cancel), [this] { closeBox(); }); @@ -350,7 +400,7 @@ MTPChannelBannedRights EditRestrictedBox::DefaultRights(gsl::not_null(month, highlighted, [this](const QDate &date) { setRestrictUntil(static_cast(QDateTime(date).toTime_t())); }), KeepOtherLayers); _restrictUntilBox->setMaxDate(QDate::currentDate().addDays(kMaxRestrictDelayDays)); @@ -358,16 +408,74 @@ void EditRestrictedBox::showRestrictUntil() { _restrictUntilBox->addLeftButton(langFactory(lng_rights_chat_banned_forever), [this] { setRestrictUntil(0); }); } -void EditRestrictedBox::setRestrictUntil(int32 until) { +void EditRestrictedBox::setRestrictUntil(TimeId until) { _until = until; if (_restrictUntilBox) { _restrictUntilBox->closeBox(); } - auto untilText = [this] { - if (isUntilForever()) { - return lang(lng_rights_chat_banned_forever); - } - return langDayOfMonthFull(date(_until).date()); - }; - _restrictUntil->setText(lng_rights_chat_banned_until(lt_when, untilText())); + clearVariants(); + createUntilGroup(); + createUntilVariants(); + resizeToContent(); +} + +void EditRestrictedBox::clearVariants() { + for (auto &&widget : base::take(_untilVariants)) { + removeControl(widget.data()); + } +} + +void EditRestrictedBox::createUntilGroup() { + _untilGroup = std::make_shared(isUntilForever() ? 0 : _until); + _untilGroup->setChangedCallback([this](int value) { + if (value == kUntilCustom) { + _untilGroup->setValue(_until); + showRestrictUntil(); + } else if (_until != value) { + _until = value; + } + }); +} + +void EditRestrictedBox::createUntilVariants() { + auto addVariant = [this](int value, const QString &text) { + _untilVariants.push_back(addControl(object_ptr(this, _untilGroup, value, text, st::defaultBoxCheckbox), st::rightsToggleMargin)); + }; + auto addCustomVariant = [this, addVariant](TimeId until, TimeId from, TimeId to) { + if (!ChannelData::IsRestrictedForever(until) && until > from && until <= to) { + addVariant(until, lng_rights_chat_banned_custom_date(lt_date, langDayOfMonthFull(date(until).date()))); + } + }; + auto addCurrentVariant = [this, addCustomVariant](TimeId from, TimeId to) { + auto oldUntil = _oldRights.c_channelBannedRights().vuntil_date.v; + if (oldUntil < _until) { + addCustomVariant(oldUntil, from, to); + } + addCustomVariant(_until, from, to); + if (oldUntil > _until) { + addCustomVariant(oldUntil, from, to); + } + }; + addVariant(0, lang(lng_rights_chat_banned_forever)); + + auto now = unixtime(); + auto nextDay = now + kSecondsInDay; + auto nextWeek = now + kSecondsInWeek; + addCurrentVariant(0, nextDay); + addVariant(kUntilOneDay, lng_rights_chat_banned_day(lt_count, 1)); + addCurrentVariant(nextDay, nextWeek); + addVariant(kUntilOneWeek, lng_rights_chat_banned_week(lt_count, 1)); + addCurrentVariant(nextWeek, INT_MAX); + addVariant(kUntilCustom, lang(lng_rights_chat_banned_custom)); +} + +TimeId EditRestrictedBox::getRealUntilValue() const { + Expects(_until != kUntilCustom); + if (_until == kUntilOneDay) { + return unixtime() + kSecondsInDay; + } else if (_until == kUntilOneWeek) { + return unixtime() + kSecondsInWeek; + } + t_assert(_until >= 0); + return _until; } diff --git a/Telegram/SourceFiles/boxes/edit_participant_box.h b/Telegram/SourceFiles/boxes/edit_participant_box.h index 637f16ac8..d37355d1a 100644 --- a/Telegram/SourceFiles/boxes/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/edit_participant_box.h @@ -26,6 +26,8 @@ namespace Ui { class FlatLabel; class LinkButton; class Checkbox; +class Radiobutton; +class RadiobuttonGroup; } // namespace Ui class CalendarBox; @@ -47,12 +49,16 @@ protected: } template - QPointer addControl(object_ptr row); + QPointer addControl(object_ptr widget, QMargins margin); + + void removeControl(QPointer widget); bool hasAdminRights() const { return _hasAdminRights; } + class Divider; + private: gsl::not_null _channel; gsl::not_null _user; @@ -106,18 +112,28 @@ private: void applyDependencies(QPointer changed); void showRestrictUntil(); - void setRestrictUntil(int32 until); + void setRestrictUntil(TimeId until); bool isUntilForever() { return ChannelData::IsRestrictedForever(_until); } + void clearVariants(); + void createUntilGroup(); + void createUntilVariants(); + TimeId getRealUntilValue() const; const MTPChannelBannedRights _oldRights; - int32 _until = 0; + TimeId _until = 0; std::vector> _dependencies; base::lambda _saveCallback; std::map> _checkboxes; - QPointer _restrictUntil; + + std::shared_ptr _untilGroup; + QVector> _untilVariants; QPointer _restrictUntilBox; + static constexpr auto kUntilOneDay = -1; + static constexpr auto kUntilOneWeek = -2; + static constexpr auto kUntilCustom = -3; + }; diff --git a/Telegram/SourceFiles/history/history_admin_log_filter.cpp b/Telegram/SourceFiles/history/history_admin_log_filter.cpp index 3a4def41d..02759901a 100644 --- a/Telegram/SourceFiles/history/history_admin_log_filter.cpp +++ b/Telegram/SourceFiles/history/history_admin_log_filter.cpp @@ -370,7 +370,7 @@ void FilterBox::prepare() { _inner->resizeToWidth(st::boxWideWidth); refreshButtons(); - setDimensions(st::boxWideWidth, _inner->height()); + setDimensions(st::boxWideWidth, qMin(_inner->height(), st::boxMaxListHeight)); } void FilterBox::refreshButtons() {