From 18805a5ef8780284aae6f6a4a0f396d904189d02 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 20 Mar 2020 15:39:33 +0400 Subject: [PATCH] Allow reordering filters in side bar. --- Telegram/SourceFiles/api/api_chat_filters.cpp | 16 ++ Telegram/SourceFiles/api/api_chat_filters.h | 8 +- .../window/window_filters_menu.cpp | 198 ++++++++++++------ .../SourceFiles/window/window_filters_menu.h | 24 +++ Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- 6 files changed, 179 insertions(+), 71 deletions(-) diff --git a/Telegram/SourceFiles/api/api_chat_filters.cpp b/Telegram/SourceFiles/api/api_chat_filters.cpp index 68b0dbebe..8af6a2fa5 100644 --- a/Telegram/SourceFiles/api/api_chat_filters.cpp +++ b/Telegram/SourceFiles/api/api_chat_filters.cpp @@ -32,4 +32,20 @@ void SaveNewFilterPinned( } +void SaveNewOrder( + not_null session, + const std::vector &order) { + auto &filters = session->data().chatsFilters(); + auto ids = QVector(); + ids.reserve(order.size()); + for (const auto id : order) { + ids.push_back(MTP_int(id)); + } + const auto wrapped = MTP_vector(ids); + filters.apply(MTP_updateDialogFilterOrder(wrapped)); + session->api().request(MTPmessages_UpdateDialogFiltersOrder( + wrapped + )).send(); +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_chat_filters.h b/Telegram/SourceFiles/api/api_chat_filters.h index 286ca2945..893002586 100644 --- a/Telegram/SourceFiles/api/api_chat_filters.h +++ b/Telegram/SourceFiles/api/api_chat_filters.h @@ -13,6 +13,12 @@ class Session; namespace Api { -void SaveNewFilterPinned(not_null session, FilterId filterId); +void SaveNewFilterPinned( + not_null session, + FilterId filterId); + +void SaveNewOrder( + not_null session, + const std::vector &order); } // namespace Api diff --git a/Telegram/SourceFiles/window/window_filters_menu.cpp b/Telegram/SourceFiles/window/window_filters_menu.cpp index 4c77580af..795169e52 100644 --- a/Telegram/SourceFiles/window/window_filters_menu.cpp +++ b/Telegram/SourceFiles/window/window_filters_menu.cpp @@ -16,21 +16,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/filters/manage_filters_box.h" #include "lang/lang_keys.h" #include "ui/filter_icons.h" +#include "ui/wrap/vertical_layout_reorder.h" +#include "api/api_chat_filters.h" #include "styles/style_widgets.h" #include "styles/style_window.h" namespace Window { -namespace { - -using Icon = Ui::FilterIcon; - -} // namespace FiltersMenu::FiltersMenu( not_null parent, not_null session) : _session(session) , _parent(parent) +, _manage(std::make_unique(_session)) , _outer(_parent) , _menu(&_outer, QString(), st::windowFiltersMainMenu) , _scroll(&_outer) @@ -40,24 +38,17 @@ FiltersMenu::FiltersMenu( setup(); } +FiltersMenu::~FiltersMenu() = default; + void FiltersMenu::setup() { _outer.setAttribute(Qt::WA_OpaquePaintEvent); _outer.show(); _outer.paintRequest( ) | rpl::start_with_next([=](QRect clip) { - const auto bottom = _scroll.y() + _container->height(); - const auto height = _outer.height() - bottom; - if (height <= 0) { - return; - } - const auto fill = clip.intersected( - QRect(0, bottom, _outer.width(), height)); - if (!fill.isEmpty()) { - auto p = QPainter(&_outer); - p.setPen(Qt::NoPen); - p.setBrush(st::windowFiltersButton.textBg); - p.drawRect(fill); - } + auto p = QPainter(&_outer); + p.setPen(Qt::NoPen); + p.setBrush(st::windowFiltersButton.textBg); + p.drawRect(clip); }, _outer.lifetime()); _parent->heightValue( @@ -89,11 +80,15 @@ void FiltersMenu::setup() { const auto i = _filters.find(_activeFilterId); if (i != end(_filters)) { i->second->setActive(false); + } else if (!_activeFilterId) { + _all->setActive(false); } _activeFilterId = id; const auto j = _filters.find(_activeFilterId); if (j != end(_filters)) { j->second->setActive(true); + } else if (!_activeFilterId) { + _all->setActive(true); } }, _outer.lifetime()); @@ -107,58 +102,125 @@ void FiltersMenu::refresh() { if (filters->list().empty()) { return; } - const auto manage = _outer.lifetime().make_state( - _session); - auto now = base::flat_map>(); - const auto prepare = [&]( - FilterId id, - const QString &title, - Icon icon, - const QString &badge) { - auto button = base::unique_qptr(_container->add( - object_ptr( - _container, - title, - st::windowFiltersButton))); - const auto &icons = Ui::LookupFilterIcon(icon); - button->setIconOverride(icons.normal, icons.active); - if (id > 0) { - const auto list = filters->chatsList(id); - rpl::single(rpl::empty_value()) | rpl::then( - list->unreadStateChanges( - ) | rpl::map([] { return rpl::empty_value(); }) - ) | rpl::start_with_next([=, raw = button.get()] { - const auto &state = list->unreadState(); - const auto count = (state.chats + state.marks); - const auto muted = (state.chatsMuted + state.marksMuted); - const auto string = !count - ? QString() - : (count > 99) - ? "..." - : QString::number(count); - raw->setBadge(string, count == muted); - }, button->lifetime()); - } - button->setActive(_session->activeChatsFilterCurrent() == id); - button->setClickedCallback([=] { - if (id >= 0) { - _session->setActiveChatsFilter(id); - } else { - manage->showBox(); - } - }); - now.emplace(id, std::move(button)); - }; - prepare(0, tr::lng_filters_all(tr::now), Icon::All, {}); - for (const auto filter : filters->list()) { - prepare( - filter.id(), - filter.title(), - Ui::ComputeFilterIcon(filter), - QString()); + + if (!_list) { + setupList(); + } + _reorder->cancel(); + auto now = base::flat_map>(); + for (const auto filter : filters->list()) { + now.emplace( + filter.id(), + prepareButton( + _list, + filter.id(), + filter.title(), + Ui::ComputeFilterIcon(filter))); } - prepare(-1, tr::lng_filters_setup(tr::now), Icon::Setup, {}); _filters = std::move(now); + _reorder->start(); + + _container->resizeToWidth(_outer.width()); +} + +void FiltersMenu::setupList() { + _all = prepareButton( + _container, + 0, + tr::lng_filters_all(tr::now), + Ui::FilterIcon::All); + _list = _container->add(object_ptr(_container)); + _setup = prepareButton( + _container, + -1, + tr::lng_filters_setup(tr::now), + Ui::FilterIcon::Setup); + _reorder = std::make_unique(_list); + + _reorder->updates( + ) | rpl::start_with_next([=](Ui::VerticalLayoutReorder::Single data) { + using State = Ui::VerticalLayoutReorder::State; + if (data.state == State::Started) { + ++_reordering; + } else { + Ui::PostponeCall(&_outer, [=] { + --_reordering; + }); + if (data.state == State::Applied) { + applyReorder(data.widget, data.oldPosition, data.newPosition); + } + } + }, _outer.lifetime()); +} + +base::unique_qptr FiltersMenu::prepareButton( + not_null container, + FilterId id, + const QString &title, + Ui::FilterIcon icon) { + auto button = base::unique_qptr(container->add( + object_ptr( + container, + title, + st::windowFiltersButton))); + const auto raw = button.get(); + const auto &icons = Ui::LookupFilterIcon(icon); + raw->setIconOverride(icons.normal, icons.active); + if (id > 0) { + const auto filters = &_session->session().data().chatsFilters(); + const auto list = filters->chatsList(id); + rpl::single(rpl::empty_value()) | rpl::then( + list->unreadStateChanges( + ) | rpl::map([] { return rpl::empty_value(); }) + ) | rpl::start_with_next([=] { + const auto &state = list->unreadState(); + const auto count = (state.chats + state.marks); + const auto muted = (state.chatsMuted + state.marksMuted); + const auto string = !count + ? QString() + : (count > 99) + ? "..." + : QString::number(count); + raw->setBadge(string, count == muted); + }, raw->lifetime()); + } + raw->setActive(_session->activeChatsFilterCurrent() == id); + raw->setClickedCallback([=] { + if (_reordering) { + return; + } else if (id >= 0) { + _session->setActiveChatsFilter(id); + } else { + _manage->showBox(); + } + }); + return button; +} + +void FiltersMenu::applyReorder( + not_null widget, + int oldPosition, + int newPosition) { + if (newPosition == oldPosition) { + return; + } + + const auto filters = &_session->session().data().chatsFilters(); + const auto &list = filters->list(); + Assert(oldPosition >= 0 && oldPosition < list.size()); + Assert(newPosition >= 0 && newPosition < list.size()); + const auto id = list[oldPosition].id(); + const auto i = _filters.find(id); + Assert(i != end(_filters)); + Assert(i->second == widget); + + auto order = ranges::view::all( + list + ) | ranges::view::transform( + &Data::ChatFilter::id + ) | ranges::to_vector; + base::reorder(order, oldPosition, newPosition); + Api::SaveNewOrder(&_session->session(), order); } } // namespace Window diff --git a/Telegram/SourceFiles/window/window_filters_menu.h b/Telegram/SourceFiles/window/window_filters_menu.h index 985fc9af2..9ebbaec5b 100644 --- a/Telegram/SourceFiles/window/window_filters_menu.h +++ b/Telegram/SourceFiles/window/window_filters_menu.h @@ -11,6 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/scroll_area.h" #include "ui/wrap/vertical_layout.h" +class ManageFiltersPrepare; + +namespace Ui { +class VerticalLayoutReorder; +enum class FilterIcon : uchar; +} // namespace Ui + namespace Window { class SessionController; @@ -20,19 +27,36 @@ public: FiltersMenu( not_null parent, not_null session); + ~FiltersMenu(); private: void setup(); void refresh(); + void setupList(); + void applyReorder( + not_null widget, + int oldPosition, + int newPosition); + [[nodiscard]] base::unique_qptr prepareButton( + not_null container, + FilterId id, + const QString &title, + Ui::FilterIcon icon); const not_null _session; const not_null _parent; + std::unique_ptr _manage; Ui::RpWidget _outer; Ui::SideBarButton _menu; Ui::ScrollArea _scroll; not_null _container; + Ui::VerticalLayout *_list = nullptr; + std::unique_ptr _reorder; + base::unique_qptr _all; + base::unique_qptr _setup; base::flat_map> _filters; FilterId _activeFilterId = 0; + int _reordering = 0; }; diff --git a/Telegram/lib_base b/Telegram/lib_base index 62d4145ba..7de25679d 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 62d4145ba04d8b6205fe0413318bc5a0f19f9410 +Subproject commit 7de25679dd68f3fb7d2faee77ff5311f4d9587d6 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index ec6744022..b051948e6 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit ec6744022c1a86f3bead192f9649c10dc424a98b +Subproject commit b051948e6947b97394df9f0f8b24e8bf407e596c