Allow reordering filters in side bar.

This commit is contained in:
John Preston 2020-03-20 15:39:33 +04:00
parent ba6373a0ae
commit 18805a5ef8
6 changed files with 179 additions and 71 deletions

View File

@ -32,4 +32,20 @@ void SaveNewFilterPinned(
}
void SaveNewOrder(
not_null<Main::Session*> session,
const std::vector<FilterId> &order) {
auto &filters = session->data().chatsFilters();
auto ids = QVector<MTPint>();
ids.reserve(order.size());
for (const auto id : order) {
ids.push_back(MTP_int(id));
}
const auto wrapped = MTP_vector<MTPint>(ids);
filters.apply(MTP_updateDialogFilterOrder(wrapped));
session->api().request(MTPmessages_UpdateDialogFiltersOrder(
wrapped
)).send();
}
} // namespace Api

View File

@ -13,6 +13,12 @@ class Session;
namespace Api {
void SaveNewFilterPinned(not_null<Main::Session*> session, FilterId filterId);
void SaveNewFilterPinned(
not_null<Main::Session*> session,
FilterId filterId);
void SaveNewOrder(
not_null<Main::Session*> session,
const std::vector<FilterId> &order);
} // namespace Api

View File

@ -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<Ui::RpWidget*> parent,
not_null<SessionController*> session)
: _session(session)
, _parent(parent)
, _manage(std::make_unique<ManageFiltersPrepare>(_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<ManageFiltersPrepare>(
_session);
auto now = base::flat_map<int, base::unique_qptr<Ui::SideBarButton>>();
const auto prepare = [&](
FilterId id,
const QString &title,
Icon icon,
const QString &badge) {
auto button = base::unique_qptr<Ui::SideBarButton>(_container->add(
object_ptr<Ui::SideBarButton>(
_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<int, base::unique_qptr<Ui::SideBarButton>>();
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<Ui::VerticalLayout>(_container));
_setup = prepareButton(
_container,
-1,
tr::lng_filters_setup(tr::now),
Ui::FilterIcon::Setup);
_reorder = std::make_unique<Ui::VerticalLayoutReorder>(_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<Ui::SideBarButton> FiltersMenu::prepareButton(
not_null<Ui::VerticalLayout*> container,
FilterId id,
const QString &title,
Ui::FilterIcon icon) {
auto button = base::unique_qptr<Ui::SideBarButton>(container->add(
object_ptr<Ui::SideBarButton>(
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<Ui::RpWidget*> 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

View File

@ -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<Ui::RpWidget*> parent,
not_null<SessionController*> session);
~FiltersMenu();
private:
void setup();
void refresh();
void setupList();
void applyReorder(
not_null<Ui::RpWidget*> widget,
int oldPosition,
int newPosition);
[[nodiscard]] base::unique_qptr<Ui::SideBarButton> prepareButton(
not_null<Ui::VerticalLayout*> container,
FilterId id,
const QString &title,
Ui::FilterIcon icon);
const not_null<SessionController*> _session;
const not_null<Ui::RpWidget*> _parent;
std::unique_ptr<ManageFiltersPrepare> _manage;
Ui::RpWidget _outer;
Ui::SideBarButton _menu;
Ui::ScrollArea _scroll;
not_null<Ui::VerticalLayout*> _container;
Ui::VerticalLayout *_list = nullptr;
std::unique_ptr<Ui::VerticalLayoutReorder> _reorder;
base::unique_qptr<Ui::SideBarButton> _all;
base::unique_qptr<Ui::SideBarButton> _setup;
base::flat_map<FilterId, base::unique_qptr<Ui::SideBarButton>> _filters;
FilterId _activeFilterId = 0;
int _reordering = 0;
};

@ -1 +1 @@
Subproject commit 62d4145ba04d8b6205fe0413318bc5a0f19f9410
Subproject commit 7de25679dd68f3fb7d2faee77ff5311f4d9587d6

@ -1 +1 @@
Subproject commit ec6744022c1a86f3bead192f9649c10dc424a98b
Subproject commit b051948e6947b97394df9f0f8b24e8bf407e596c