From 1ffbc122e1172b9436655bb7af169491065fe586 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 4 Apr 2023 18:37:45 +0400 Subject: [PATCH] Allow select/deselect all in filter link boxes. --- Telegram/Resources/langs/lang.strings | 2 +- Telegram/SourceFiles/api/api_chat_filters.cpp | 44 ++++++++- .../boxes/filters/edit_filter_links.cpp | 94 ++++++++++++++++++- .../boxes/filters/edit_filter_links.h | 7 ++ 4 files changed, 138 insertions(+), 9 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4e8314d3e3..750f0c0871 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3598,7 +3598,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_filters_by_link_add_button" = "Add {folder}"; "lng_filters_by_link_add_no" = "Do not add this folder"; "lng_filters_by_link_more" = "Add Chats to Folder"; -"lng_filters_by_link_more_sure" = "Do you want to join chats and add them to your folder {folder}?"; +"lng_filters_by_link_more_sure" = "Do you want to join chats and add them to the folder {folder}?"; "lng_filters_by_link_about" = "You can deselect the chats you don't want to join."; "lng_filters_by_link_join_button" = "Join Chats"; "lng_filters_by_link_join_no" = "Do not join any chats"; diff --git a/Telegram/SourceFiles/api/api_chat_filters.cpp b/Telegram/SourceFiles/api/api_chat_filters.cpp index 1159b3c348..8e1f443298 100644 --- a/Telegram/SourceFiles/api/api_chat_filters.cpp +++ b/Telegram/SourceFiles/api/api_chat_filters.cpp @@ -63,6 +63,7 @@ private: void setupAboveWidget(); void setupBelowWidget(); void initDesiredHeightValue(); + void toggleAllSelected(bool select); const not_null _window; Ui::RpWidget *_addedTopWidget = nullptr; @@ -259,8 +260,6 @@ ToggleChatsController::ToggleChatsController( } void ToggleChatsController::prepare() { - setupAboveWidget(); - setupBelowWidget(); auto selected = base::flat_set>(); const auto add = [&](not_null peer, bool additional = false) { auto row = std::make_unique(peer); @@ -292,6 +291,8 @@ void ToggleChatsController::prepare() { for (const auto &peer : _additional) { add(peer, true); } + setupAboveWidget(); + setupBelowWidget(); initDesiredHeightValue(); delegate()->peerListRefreshRows(); _selected = std::move(selected); @@ -339,7 +340,13 @@ void ToggleChatsController::setupAboveWidget() { : _chats.empty() ? _additional.size() : _chats.size(); - AddSubsectionTitle( + const auto selectableCount = delegate()->peerListFullRowsCount() + - (_action == ToggleAction::Adding ? int(_additional.size()) : 0); + auto selectedCount = _selected.value( + ) | rpl::map([](const base::flat_set> &selected) { + return int(selected.size()); + }); + AddFilterSubtitleWithToggles( realAbove, (_action == ToggleAction::Removing ? tr::lng_filters_by_link_quit @@ -348,12 +355,41 @@ void ToggleChatsController::setupAboveWidget() { : tr::lng_filters_by_link_join)( lt_count, rpl::single(float64(count))), - st::filterLinkSubsectionTitlePadding); + selectableCount, + std::move(selectedCount), + [=](bool select) { toggleAllSelected(select); }); _aboveHeight = realAbove->heightValue(); delegate()->peerListSetAboveWidget(std::move(wrap)); } +void ToggleChatsController::toggleAllSelected(bool select) { + auto selected = _selected.current(); + if (!select) { + if (selected.empty()) { + return; + } + for (const auto &peer : selected) { + const auto row = delegate()->peerListFindRow(peer->id.value); + Assert(row != nullptr); + delegate()->peerListSetRowChecked(row, false); + } + selected = {}; + } else { + const auto count = delegate()->peerListFullRowsCount(); + for (auto i = 0; i != count; ++i) { + const auto row = delegate()->peerListRowAt(i); + const auto peer = row->peer(); + if (_action != ToggleAction::Adding || + !ranges::contains(_additional, peer)) { + delegate()->peerListSetRowChecked(row, true); + selected.emplace(peer); + } + } + } + _selected = std::move(selected); +} + void ToggleChatsController::setupBelowWidget() { if (_chats.empty()) { auto widget = object_ptr((QWidget*)nullptr); diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp index c1bea9990a..5a788cb455 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/controls/invite_link_label.h" #include "ui/text/text_utilities.h" #include "ui/toasts/common_toasts.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/popup_menu.h" #include "ui/wrap/vertical_layout.h" @@ -507,6 +508,7 @@ private: void setupBelowWidget(); void addHeader(not_null container); void addLinkBlock(not_null container); + void toggleAllSelected(bool select); const not_null _window; InviteLinkData _data; @@ -684,8 +686,6 @@ void LinkController::addLinkBlock(not_null container) { void LinkController::prepare() { Expects(!_data.url.isEmpty() || _data.chats.empty()); - setupAboveWidget(); - setupBelowWidget(); for (const auto &history : _data.chats) { const auto peer = history->peer; auto row = std::make_unique( @@ -716,6 +716,8 @@ void LinkController::prepare() { _denied.emplace(peer); } } + setupAboveWidget(); + setupBelowWidget(); delegate()->peerListRefreshRows(); _selected = _initial; } @@ -744,6 +746,34 @@ void LinkController::rowClicked(not_null row) { } } +void LinkController::toggleAllSelected(bool select) { + auto selected = _selected.current(); + if (!select) { + if (selected.empty()) { + return; + } + for (const auto &peer : selected) { + const auto row = delegate()->peerListFindRow(peer->id.value); + Assert(row != nullptr); + delegate()->peerListSetRowChecked(row, false); + } + selected = {}; + } else { + const auto count = delegate()->peerListFullRowsCount(); + for (auto i = 0; i != count; ++i) { + const auto row = delegate()->peerListRowAt(i); + const auto peer = row->peer(); + if (!_denied.contains(peer)) { + delegate()->peerListSetRowChecked(row, true); + selected.emplace(peer); + } + } + } + const auto has = (_initial != selected); + _selected = std::move(selected); + _hasChanges = has; +} + void LinkController::showFinished() { _showFinished.fire({}); } @@ -770,10 +800,18 @@ void LinkController::setupAboveWidget() { lt_count, float64(selected.size())); }); - Settings::AddSubsectionTitle( + const auto mayBeSelected = delegate()->peerListFullRowsCount() + - int(_denied.size()); + auto selectedCount = _selected.value( + ) | rpl::map([](const base::flat_set> &selected) { + return int(selected.size()); + }); + AddFilterSubtitleWithToggles( container, std::move(subtitle), - st::filterLinkSubsectionTitlePadding); + mayBeSelected, + std::move(selectedCount), + [=](bool select) { toggleAllSelected(select); }); // Fix label cutting on text change from smaller to longer. _selected.changes() | rpl::start_with_next([=] { @@ -1206,3 +1244,51 @@ void SetupFilterLinks( delegate->setContent(content); controller->setDelegate(delegate); } + +void AddFilterSubtitleWithToggles( + not_null container, + rpl::producer text, + int selectableCount, + rpl::producer selectedCount, + Fn toggle) { + using namespace rpl::mappers; + + const auto selectable = (selectableCount > 0); + auto padding = st::filterLinkSubsectionTitlePadding; + if (selectable) { + const auto font = st::boxLinkButton.font; + padding.setRight(padding.right() + font->spacew + std::max( + font->width(tr::lng_filters_by_link_select(tr::now)), + font->width(tr::lng_filters_by_link_deselect(tr::now)))); + } + const auto title = Settings::AddSubsectionTitle( + container, + std::move(text), + padding); + if (!selectable) { + return; + } + const auto link = Ui::CreateChild( + container.get(), + tr::lng_filters_by_link_select(tr::now), + st::boxLinkButton); + const auto canSelect = link->lifetime().make_state>( + std::move(selectedCount) | rpl::map(_1 < selectableCount)); + canSelect->value( + ) | rpl::start_with_next([=](bool can) { + link->setText(can + ? tr::lng_filters_by_link_select(tr::now) + : tr::lng_filters_by_link_deselect(tr::now)); + }, link->lifetime()); + link->setClickedCallback([=] { + toggle(canSelect->current()); + }); + + rpl::combine( + container->widthValue(), + title->topValue(), + link->widthValue() + ) | rpl::start_with_next([=](int outer, int y, int width) { + link->move(outer - st::boxRowPadding.right() - width, y); + }, link->lifetime()); +} diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_links.h b/Telegram/SourceFiles/boxes/filters/edit_filter_links.h index dca57121a7..802e15bea2 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_links.h +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_links.h @@ -47,3 +47,10 @@ void SetupFilterLinks( not_null window, rpl::producer> value, Fn currentFilter); + +void AddFilterSubtitleWithToggles( + not_null container, + rpl::producer text, + int selectableCount, + rpl::producer selectedCount, + Fn toggle);