From 1ae3fc86da54c5fedbd151d29dd68bb165beefc2 Mon Sep 17 00:00:00 2001 From: James Graham Date: Wed, 28 Feb 2024 17:57:32 +0000 Subject: [PATCH] Updated room sorting Change the LastActivity sort order to Activity and update to a more flexible way of sorting based on an order from model roles. Add options to actually switch between Alphabetical and Activity Based on some old work by @tdfischer implements #103 --- src/models/roomtreemodel.cpp | 12 +++- src/models/roomtreemodel.h | 2 + src/models/sortfilterroomtreemodel.cpp | 89 ++++++++++++++++++-------- src/models/sortfilterroomtreemodel.h | 19 ++---- src/neochatconfig.kcfg | 4 ++ src/qml/GeneralSettingsPage.qml | 23 +++++++ src/qml/RoomListPage.qml | 1 - 7 files changed, 107 insertions(+), 43 deletions(-) diff --git a/src/models/roomtreemodel.cpp b/src/models/roomtreemodel.cpp index 521e14f0a..d78e644de 100644 --- a/src/models/roomtreemodel.cpp +++ b/src/models/roomtreemodel.cpp @@ -220,6 +220,8 @@ QHash RoomTreeModel::roleNames() const roles[IsDirectChat] = "isDirectChat"; roles[DelegateTypeRole] = "delegateType"; roles[IconRole] = "icon"; + roles[AttentionRole] = "attention"; + roles[FavouriteRole] = "favourite"; return roles; } @@ -270,10 +272,10 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const return NeoChatRoomType::typeForRoom(room); } if (role == NotificationCountRole) { - return room->notificationCount(); + return int(room->notificationCount()); } if (role == HighlightCountRole) { - return room->highlightCount(); + return int(room->highlightCount()); } if (role == LastActiveTimeRole) { return room->lastActiveTime(); @@ -315,6 +317,12 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const if (role == DelegateTypeRole) { return QStringLiteral("normal"); } + if (role == AttentionRole) { + return room->notificationCount() + room->highlightCount() > 0; + } + if (role == FavouriteRole) { + return room->isFavourite(); + } return {}; } diff --git a/src/models/roomtreemodel.h b/src/models/roomtreemodel.h index 52ac23d2e..2a28caa7a 100644 --- a/src/models/roomtreemodel.h +++ b/src/models/roomtreemodel.h @@ -47,6 +47,8 @@ public: IsDirectChat, /**< Whether this room is a direct chat. */ DelegateTypeRole, IconRole, + AttentionRole, /**< Whether there are any notifications. */ + FavouriteRole, /**< Whether the room is favourited. */ }; Q_ENUM(EventRoles) explicit RoomTreeModel(QObject *parent = nullptr); diff --git a/src/models/sortfilterroomtreemodel.cpp b/src/models/sortfilterroomtreemodel.cpp index b586583c9..539ebe3b5 100644 --- a/src/models/sortfilterroomtreemodel.cpp +++ b/src/models/sortfilterroomtreemodel.cpp @@ -13,6 +13,12 @@ SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent) : QSortFilterProxyModel(parent) { + setRoomSortOrder(static_cast(NeoChatConfig::sortOrder())); + connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, [this]() { + setRoomSortOrder(static_cast(NeoChatConfig::sortOrder())); + invalidateFilter(); + }); + setRecursiveFilteringEnabled(true); sort(0); invalidateFilter(); @@ -29,48 +35,75 @@ SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent) void SortFilterRoomTreeModel::setRoomSortOrder(SortFilterRoomTreeModel::RoomSortOrder sortOrder) { m_sortOrder = sortOrder; - Q_EMIT roomSortOrderChanged(); if (sortOrder == SortFilterRoomTreeModel::Alphabetical) { setSortRole(RoomTreeModel::DisplayNameRole); - } else if (sortOrder == SortFilterRoomTreeModel::LastActivity) { + } else if (sortOrder == SortFilterRoomTreeModel::Activity) { setSortRole(RoomTreeModel::LastActiveTimeRole); } invalidate(); } -SortFilterRoomTreeModel::RoomSortOrder SortFilterRoomTreeModel::roomSortOrder() const +static const QVector alphabeticalSortPriorities{ + // Does exactly what it says on the tin. + RoomTreeModel::DisplayNameRole, +}; + +static const QVector activitySortPriorities{ + // Anything useful at the top, quiet rooms at the bottom + RoomTreeModel::AttentionRole, + // Organize by highlights, notifications, unread favorites, all other unread, in that order + RoomTreeModel::HighlightCountRole, + RoomTreeModel::NotificationCountRole, + RoomTreeModel::FavouriteRole, + // Finally sort by last activity time + RoomTreeModel::LastActiveTimeRole, +}; + +bool SortFilterRoomTreeModel::roleCmp(const QVariant &sortLeft, const QVariant &sortRight) const { - return m_sortOrder; + switch (sortLeft.typeId()) { + case QMetaType::Bool: + return (sortLeft == sortRight) ? false : sortLeft.toBool(); + case QMetaType::QString: + return sortLeft.toString() < sortRight.toString(); + case QMetaType::Int: + return sortLeft.toInt() > sortRight.toInt(); + case QMetaType::QDateTime: + return sortLeft.toDateTime() > sortRight.toDateTime(); + default: + return false; + } +} + +bool SortFilterRoomTreeModel::prioritiesCmp(const QVector &priorities, + const QModelIndex &source_left, + const QModelIndex &source_right) const +{ + for (RoomTreeModel::EventRoles sortRole : priorities) { + const auto sortLeft = sourceModel()->data(source_left, sortRole); + const auto sortRight = sourceModel()->data(source_right, sortRole); + if (sortLeft != sortRight) { + return roleCmp(sortLeft, sortRight); + } + } + return QSortFilterProxyModel::lessThan(source_left, source_right); } bool SortFilterRoomTreeModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { - if (m_sortOrder == SortFilterRoomTreeModel::LastActivity) { - // display favorite rooms always on top - const auto categoryLeft = static_cast(sourceModel()->data(source_left, RoomTreeModel::CategoryRole).toInt()); - const auto categoryRight = static_cast(sourceModel()->data(source_right, RoomTreeModel::CategoryRole).toInt()); + // Don't sort the top level categories. + if (!source_left.parent().isValid() || !source_right.parent().isValid()) { + return false; + } - if (categoryLeft == NeoChatRoomType::Types::Favorite && categoryRight == NeoChatRoomType::Types::Favorite) { - return sourceModel()->data(source_left, RoomTreeModel::LastActiveTimeRole).toDateTime() - > sourceModel()->data(source_right, RoomTreeModel::LastActiveTimeRole).toDateTime(); - } - if (categoryLeft == NeoChatRoomType::Types::Favorite) { - return true; - } else if (categoryRight == NeoChatRoomType::Types::Favorite) { - return false; - } + switch (m_sortOrder) { + case SortFilterRoomTreeModel::Alphabetical: + return prioritiesCmp(alphabeticalSortPriorities, source_left, source_right); + case SortFilterRoomTreeModel::Activity: + return prioritiesCmp(activitySortPriorities, source_left, source_right); + } - return sourceModel()->data(source_left, RoomTreeModel::LastActiveTimeRole).toDateTime() - > sourceModel()->data(source_right, RoomTreeModel::LastActiveTimeRole).toDateTime(); - } - if (m_sortOrder != SortFilterRoomTreeModel::Categories) { - return QSortFilterProxyModel::lessThan(source_left, source_right); - } - if (sourceModel()->data(source_left, RoomTreeModel::CategoryRole) != sourceModel()->data(source_right, RoomTreeModel::CategoryRole)) { - return sourceModel()->data(source_left, RoomTreeModel::CategoryRole).toInt() < sourceModel()->data(source_right, RoomTreeModel::CategoryRole).toInt(); - } - return sourceModel()->data(source_left, RoomTreeModel::LastActiveTimeRole).toDateTime() - > sourceModel()->data(source_right, RoomTreeModel::LastActiveTimeRole).toDateTime(); + return QSortFilterProxyModel::lessThan(source_left, source_right); } void SortFilterRoomTreeModel::setFilterText(const QString &text) diff --git a/src/models/sortfilterroomtreemodel.h b/src/models/sortfilterroomtreemodel.h index 19bf5d613..a0632a2e3 100644 --- a/src/models/sortfilterroomtreemodel.h +++ b/src/models/sortfilterroomtreemodel.h @@ -7,6 +7,8 @@ #include #include +#include "models/roomtreemodel.h" + /** * @class SortFilterRoomTreeModel * @@ -31,13 +33,6 @@ class SortFilterRoomTreeModel : public QSortFilterProxyModel Q_OBJECT QML_ELEMENT - /** - * @brief The order by which the rooms will be sorted. - * - * @sa RoomSortOrder - */ - Q_PROPERTY(RoomSortOrder roomSortOrder READ roomSortOrder WRITE setRoomSortOrder NOTIFY roomSortOrderChanged) - /** * @brief The text to use to filter room names. */ @@ -56,8 +51,7 @@ class SortFilterRoomTreeModel : public QSortFilterProxyModel public: enum RoomSortOrder { Alphabetical, - LastActivity, - Categories, + Activity, }; Q_ENUM(RoomSortOrder) @@ -71,7 +65,6 @@ public: explicit SortFilterRoomTreeModel(QObject *parent = nullptr); void setRoomSortOrder(RoomSortOrder sortOrder); - [[nodiscard]] RoomSortOrder roomSortOrder() const; void setFilterText(const QString &text); [[nodiscard]] QString filterText() const; @@ -98,14 +91,16 @@ protected: [[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; Q_SIGNALS: - void roomSortOrderChanged(); void filterTextChanged(); void activeSpaceIdChanged(); void modeChanged(); private: - RoomSortOrder m_sortOrder = Categories; + RoomSortOrder m_sortOrder = Activity; Mode m_mode = All; QString m_filterText; QString m_activeSpaceId; + + bool roleCmp(const QVariant &left, const QVariant &right) const; + bool prioritiesCmp(const QVector &priorities, const QModelIndex &left, const QModelIndex &right) const; }; diff --git a/src/neochatconfig.kcfg b/src/neochatconfig.kcfg index 5fd110abf..974502bb1 100644 --- a/src/neochatconfig.kcfg +++ b/src/neochatconfig.kcfg @@ -118,6 +118,10 @@ false + + + 1 + diff --git a/src/qml/GeneralSettingsPage.qml b/src/qml/GeneralSettingsPage.qml index 879e18630..a5496ef54 100644 --- a/src/qml/GeneralSettingsPage.qml +++ b/src/qml/GeneralSettingsPage.qml @@ -65,6 +65,29 @@ FormCard.FormCardPage { } } } + FormCard.FormHeader { + title: i18n("Room list sort order") + } + FormCard.FormCard { + FormCard.FormRadioDelegate { + text: i18nc("As in 'sort something based on last activity'", "Activity") + checked: Config.sortOrder === 1 + enabled: !Config.isSortOrderImmutable + onToggled: { + Config.sortOrder = 1 + Config.save() + } + } + FormCard.FormRadioDelegate { + text: i18nc("As in 'sort something alphabetically'", "Alphabetical") + checked: Config.sortOrder === 0 + enabled: !Config.isSortOrderImmutable + onToggled: { + Config.sortOrder = 0 + Config.save() + } + } + } FormCard.FormHeader { title: i18n("Timeline Events") } diff --git a/src/qml/RoomListPage.qml b/src/qml/RoomListPage.qml index 686350bf1..d13e93811 100644 --- a/src/qml/RoomListPage.qml +++ b/src/qml/RoomListPage.qml @@ -148,7 +148,6 @@ Kirigami.Page { property bool filterTextJustChanged: false sourceModel: root.roomTreeModel - roomSortOrder: SortFilterRoomTreeModel.Categories activeSpaceId: spaceDrawer.selectedSpaceId mode: spaceDrawer.showDirectChats ? SortFilterRoomTreeModel.DirectChats : SortFilterRoomTreeModel.Rooms }