From 54c7eb374a9d850ff8050077be57fafdff4531e9 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 21 Jul 2018 13:35:36 +0300 Subject: [PATCH] Lower the complexity of the group filtering algorithm (#380) The old algorithm during filtering will check every room if it's part of the group. O(N*G) The room ids for a group are now stored in a map for faster lookup so the search can be completed in a single pass. O(N) --- src/ChatPage.cpp | 4 +-- src/CommunitiesList.cpp | 10 ++++---- src/CommunitiesList.h | 4 +-- src/CommunitiesListItem.h | 6 ++--- src/RoomList.cpp | 53 ++++++++++++++++++++++----------------- src/RoomList.h | 19 +++++++------- 6 files changed, 52 insertions(+), 44 deletions(-) diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 6f5e31e5..27c4fbe9 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -514,9 +514,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) current_community_ = groupId; if (groupId == "world") - room_list_->setFilterRooms(false); + room_list_->removeFilter(); else - room_list_->setRoomFilter(communitiesList_->roomList(groupId)); + room_list_->applyFilter(communitiesList_->roomList(groupId)); }); connect(¬ificationsManager, diff --git a/src/CommunitiesList.cpp b/src/CommunitiesList.cpp index c271be89..af30050d 100644 --- a/src/CommunitiesList.cpp +++ b/src/CommunitiesList.cpp @@ -1,5 +1,5 @@ -#include "CommunitiesList.h" #include "Cache.h" +#include "CommunitiesList.h" #include "Logging.h" #include "MatrixClient.h" @@ -83,7 +83,7 @@ CommunitiesList::addCommunity(const std::string &group_id) connect(this, &CommunitiesList::groupRoomsRetrieved, this, - [this](const QString &id, const std::vector &rooms) { + [this](const QString &id, const std::map &rooms) { if (communities_.find(id) == communities_.end()) return; @@ -109,9 +109,9 @@ CommunitiesList::addCommunity(const std::string &group_id) return; } - std::vector room_ids; + std::map room_ids; for (const auto &room : res.at("chunk")) - room_ids.push_back(QString::fromStdString(room.at("room_id"))); + room_ids.emplace(QString::fromStdString(room.at("room_id")), true); emit groupRoomsRetrieved(id, room_ids); }); @@ -185,7 +185,7 @@ CommunitiesList::fetchCommunityAvatar(const QString &id, const QString &avatarUr }); } -std::vector +std::map CommunitiesList::roomList(const QString &id) const { if (communityExists(id)) diff --git a/src/CommunitiesList.h b/src/CommunitiesList.h index 32a64bf2..d4db54cc 100644 --- a/src/CommunitiesList.h +++ b/src/CommunitiesList.h @@ -18,13 +18,13 @@ public: void addCommunity(const std::string &id); void removeCommunity(const QString &id) { communities_.erase(id); }; - std::vector roomList(const QString &id) const; + std::map roomList(const QString &id) const; signals: void communityChanged(const QString &id); void avatarRetrieved(const QString &id, const QPixmap &img); void groupProfileRetrieved(const QString &group_id, const mtx::responses::GroupProfile &); - void groupRoomsRetrieved(const QString &group_id, const std::vector &res); + void groupRoomsRetrieved(const QString &group_id, const std::map &res); public slots: void updateCommunityAvatar(const QString &id, const QPixmap &img); diff --git a/src/CommunitiesListItem.h b/src/CommunitiesListItem.h index a9b6e333..bfd54661 100644 --- a/src/CommunitiesListItem.h +++ b/src/CommunitiesListItem.h @@ -32,8 +32,8 @@ public: bool isPressed() const { return isPressed_; } void setAvatar(const QImage &img); - void setRooms(std::vector room_ids) { room_ids_ = std::move(room_ids); } - std::vector rooms() const { return room_ids_; } + void setRooms(std::map room_ids) { room_ids_ = std::move(room_ids); } + std::map rooms() const { return room_ids_; } QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; } QColor hoverBackgroundColor() const { return hoverBackgroundColor_; } @@ -69,7 +69,7 @@ private: QString resolveName() const; - std::vector room_ids_; + std::map room_ids_; QString name_; QString groupId_; diff --git a/src/RoomList.cpp b/src/RoomList.cpp index a9328984..771081b9 100644 --- a/src/RoomList.cpp +++ b/src/RoomList.cpp @@ -56,6 +56,8 @@ RoomList::RoomList(QSharedPointer userSettings, QWidget *parent) scrollArea_->setWidget(scrollAreaContents_); topLayout_->addWidget(scrollArea_); + qRegisterMetaType>(); + connect(this, &RoomList::updateRoomAvatarCb, this, &RoomList::updateRoomAvatar); } @@ -340,10 +342,21 @@ RoomList::closeJoinRoomDialog(bool isJoining, QString roomAlias) } void -RoomList::setFilterRooms(bool isFilteringEnabled) +RoomList::removeFilter() { for (int i = 0; i < contentsLayout_->count(); i++) { - // If roomFilter_ contains the room for the current RoomInfoListItem, + auto widget = + qobject_cast(contentsLayout_->itemAt(i)->widget()); + if (widget) + widget->show(); + } +} + +void +RoomList::applyFilter(const std::map &filter) +{ + for (int i = 0; i < contentsLayout_->count(); i++) { + // If filter contains the room for the current RoomInfoListItem, // show the list item, otherwise hide it auto listitem = qobject_cast(contentsLayout_->itemAt(i)->widget()); @@ -351,28 +364,29 @@ RoomList::setFilterRooms(bool isFilteringEnabled) if (!listitem) continue; - if (!isFilteringEnabled || filterItemExists(listitem->roomId())) + if (filter.find(listitem->roomId()) != filter.end()) listitem->show(); else listitem->hide(); } - if (isFilteringEnabled && !filterItemExists(selectedRoom_)) { - RoomInfoListItem *firstVisibleRoom = nullptr; + // If the already selected room is part of the group, make sure it's visible. + if (!selectedRoom_.isEmpty() && (filter.find(selectedRoom_) != filter.end())) + return; - for (int i = 0; i < contentsLayout_->count(); i++) { - QWidget *item = contentsLayout_->itemAt(i)->widget(); + selectFirstVisibleRoom(); +} - if (item != nullptr && item->isVisible()) { - firstVisibleRoom = qobject_cast(item); - break; - } +void +RoomList::selectFirstVisibleRoom() +{ + for (int i = 0; i < contentsLayout_->count(); i++) { + auto item = qobject_cast(contentsLayout_->itemAt(i)->widget()); + + if (item && item->isVisible()) { + highlightSelectedRoom(item->roomId()); + break; } - - if (firstVisibleRoom != nullptr) - highlightSelectedRoom(firstVisibleRoom->roomId()); - } else { - scrollArea_->ensureWidgetVisible(rooms_[selectedRoom_].data()); } } @@ -404,13 +418,6 @@ RoomList::updateRoom(const QString &room_id, const RoomInfo &info) room->update(); } -void -RoomList::setRoomFilter(std::vector room_ids) -{ - roomFilter_ = room_ids; - setFilterRooms(true); -} - void RoomList::addInvitedRoom(const QString &room_id, const RoomInfo &info) { diff --git a/src/RoomList.h b/src/RoomList.h index 59b0e865..d676015c 100644 --- a/src/RoomList.h +++ b/src/RoomList.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include #include @@ -33,6 +34,9 @@ class UserSettings; struct DescInfo; struct RoomInfo; +using RoomIds = std::map; +Q_DECLARE_METATYPE(RoomIds) + class RoomList : public QWidget { Q_OBJECT @@ -49,8 +53,10 @@ public: void addRoom(const QString &room_id, const RoomInfo &info); void addInvitedRoom(const QString &room_id, const RoomInfo &info); void removeRoom(const QString &room_id, bool reset); - void setFilterRooms(bool filterRooms); - void setRoomFilter(std::vector room_ids); + //! Hide rooms that are not present in the given filter. + void applyFilter(const std::map &rooms); + //! Show all the available rooms. + void removeFilter(); void updateRoom(const QString &room_id, const RoomInfo &info); void cleanupInvites(const std::map &invites); @@ -82,10 +88,8 @@ private: std::pair> firstRoom() const; void calculateUnreadMessageCount(); bool roomExists(const QString &room_id) { return rooms_.find(room_id) != rooms_.end(); } - bool filterItemExists(const QString &id) - { - return std::find(roomFilter_.begin(), roomFilter_.end(), id) != roomFilter_.end(); - } + //! Select the first visible room in the room list. + void selectFirstVisibleRoom(); QVBoxLayout *topLayout_; QVBoxLayout *contentsLayout_; @@ -99,9 +103,6 @@ private: std::map> rooms_; QString selectedRoom_; - //! Which rooms to include in the room list. - std::vector roomFilter_; - QSharedPointer userSettings_; bool isSortPending_ = false;