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
This commit is contained in:
@@ -220,6 +220,8 @@ QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
|||||||
roles[IsDirectChat] = "isDirectChat";
|
roles[IsDirectChat] = "isDirectChat";
|
||||||
roles[DelegateTypeRole] = "delegateType";
|
roles[DelegateTypeRole] = "delegateType";
|
||||||
roles[IconRole] = "icon";
|
roles[IconRole] = "icon";
|
||||||
|
roles[AttentionRole] = "attention";
|
||||||
|
roles[FavouriteRole] = "favourite";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,10 +272,10 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
return NeoChatRoomType::typeForRoom(room);
|
return NeoChatRoomType::typeForRoom(room);
|
||||||
}
|
}
|
||||||
if (role == NotificationCountRole) {
|
if (role == NotificationCountRole) {
|
||||||
return room->notificationCount();
|
return int(room->notificationCount());
|
||||||
}
|
}
|
||||||
if (role == HighlightCountRole) {
|
if (role == HighlightCountRole) {
|
||||||
return room->highlightCount();
|
return int(room->highlightCount());
|
||||||
}
|
}
|
||||||
if (role == LastActiveTimeRole) {
|
if (role == LastActiveTimeRole) {
|
||||||
return room->lastActiveTime();
|
return room->lastActiveTime();
|
||||||
@@ -315,6 +317,12 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
if (role == DelegateTypeRole) {
|
if (role == DelegateTypeRole) {
|
||||||
return QStringLiteral("normal");
|
return QStringLiteral("normal");
|
||||||
}
|
}
|
||||||
|
if (role == AttentionRole) {
|
||||||
|
return room->notificationCount() + room->highlightCount() > 0;
|
||||||
|
}
|
||||||
|
if (role == FavouriteRole) {
|
||||||
|
return room->isFavourite();
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ public:
|
|||||||
IsDirectChat, /**< Whether this room is a direct chat. */
|
IsDirectChat, /**< Whether this room is a direct chat. */
|
||||||
DelegateTypeRole,
|
DelegateTypeRole,
|
||||||
IconRole,
|
IconRole,
|
||||||
|
AttentionRole, /**< Whether there are any notifications. */
|
||||||
|
FavouriteRole, /**< Whether the room is favourited. */
|
||||||
};
|
};
|
||||||
Q_ENUM(EventRoles)
|
Q_ENUM(EventRoles)
|
||||||
explicit RoomTreeModel(QObject *parent = nullptr);
|
explicit RoomTreeModel(QObject *parent = nullptr);
|
||||||
|
|||||||
@@ -13,6 +13,12 @@
|
|||||||
SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent)
|
SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
{
|
{
|
||||||
|
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
|
||||||
|
connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, [this]() {
|
||||||
|
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
|
||||||
|
invalidateFilter();
|
||||||
|
});
|
||||||
|
|
||||||
setRecursiveFilteringEnabled(true);
|
setRecursiveFilteringEnabled(true);
|
||||||
sort(0);
|
sort(0);
|
||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
@@ -29,48 +35,75 @@ SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent)
|
|||||||
void SortFilterRoomTreeModel::setRoomSortOrder(SortFilterRoomTreeModel::RoomSortOrder sortOrder)
|
void SortFilterRoomTreeModel::setRoomSortOrder(SortFilterRoomTreeModel::RoomSortOrder sortOrder)
|
||||||
{
|
{
|
||||||
m_sortOrder = sortOrder;
|
m_sortOrder = sortOrder;
|
||||||
Q_EMIT roomSortOrderChanged();
|
|
||||||
if (sortOrder == SortFilterRoomTreeModel::Alphabetical) {
|
if (sortOrder == SortFilterRoomTreeModel::Alphabetical) {
|
||||||
setSortRole(RoomTreeModel::DisplayNameRole);
|
setSortRole(RoomTreeModel::DisplayNameRole);
|
||||||
} else if (sortOrder == SortFilterRoomTreeModel::LastActivity) {
|
} else if (sortOrder == SortFilterRoomTreeModel::Activity) {
|
||||||
setSortRole(RoomTreeModel::LastActiveTimeRole);
|
setSortRole(RoomTreeModel::LastActiveTimeRole);
|
||||||
}
|
}
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
SortFilterRoomTreeModel::RoomSortOrder SortFilterRoomTreeModel::roomSortOrder() const
|
static const QVector<RoomTreeModel::EventRoles> alphabeticalSortPriorities{
|
||||||
|
// Does exactly what it says on the tin.
|
||||||
|
RoomTreeModel::DisplayNameRole,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QVector<RoomTreeModel::EventRoles> 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<RoomTreeModel::EventRoles> &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
|
bool SortFilterRoomTreeModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||||
{
|
{
|
||||||
if (m_sortOrder == SortFilterRoomTreeModel::LastActivity) {
|
// Don't sort the top level categories.
|
||||||
// display favorite rooms always on top
|
if (!source_left.parent().isValid() || !source_right.parent().isValid()) {
|
||||||
const auto categoryLeft = static_cast<NeoChatRoomType::Types>(sourceModel()->data(source_left, RoomTreeModel::CategoryRole).toInt());
|
return false;
|
||||||
const auto categoryRight = static_cast<NeoChatRoomType::Types>(sourceModel()->data(source_right, RoomTreeModel::CategoryRole).toInt());
|
}
|
||||||
|
|
||||||
if (categoryLeft == NeoChatRoomType::Types::Favorite && categoryRight == NeoChatRoomType::Types::Favorite) {
|
switch (m_sortOrder) {
|
||||||
return sourceModel()->data(source_left, RoomTreeModel::LastActiveTimeRole).toDateTime()
|
case SortFilterRoomTreeModel::Alphabetical:
|
||||||
> sourceModel()->data(source_right, RoomTreeModel::LastActiveTimeRole).toDateTime();
|
return prioritiesCmp(alphabeticalSortPriorities, source_left, source_right);
|
||||||
}
|
case SortFilterRoomTreeModel::Activity:
|
||||||
if (categoryLeft == NeoChatRoomType::Types::Favorite) {
|
return prioritiesCmp(activitySortPriorities, source_left, source_right);
|
||||||
return true;
|
}
|
||||||
} else if (categoryRight == NeoChatRoomType::Types::Favorite) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceModel()->data(source_left, RoomTreeModel::LastActiveTimeRole).toDateTime()
|
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
||||||
> 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SortFilterRoomTreeModel::setFilterText(const QString &text)
|
void SortFilterRoomTreeModel::setFilterText(const QString &text)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#include "models/roomtreemodel.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class SortFilterRoomTreeModel
|
* @class SortFilterRoomTreeModel
|
||||||
*
|
*
|
||||||
@@ -31,13 +33,6 @@ class SortFilterRoomTreeModel : public QSortFilterProxyModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
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.
|
* @brief The text to use to filter room names.
|
||||||
*/
|
*/
|
||||||
@@ -56,8 +51,7 @@ class SortFilterRoomTreeModel : public QSortFilterProxyModel
|
|||||||
public:
|
public:
|
||||||
enum RoomSortOrder {
|
enum RoomSortOrder {
|
||||||
Alphabetical,
|
Alphabetical,
|
||||||
LastActivity,
|
Activity,
|
||||||
Categories,
|
|
||||||
};
|
};
|
||||||
Q_ENUM(RoomSortOrder)
|
Q_ENUM(RoomSortOrder)
|
||||||
|
|
||||||
@@ -71,7 +65,6 @@ public:
|
|||||||
explicit SortFilterRoomTreeModel(QObject *parent = nullptr);
|
explicit SortFilterRoomTreeModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
void setRoomSortOrder(RoomSortOrder sortOrder);
|
void setRoomSortOrder(RoomSortOrder sortOrder);
|
||||||
[[nodiscard]] RoomSortOrder roomSortOrder() const;
|
|
||||||
|
|
||||||
void setFilterText(const QString &text);
|
void setFilterText(const QString &text);
|
||||||
[[nodiscard]] QString filterText() const;
|
[[nodiscard]] QString filterText() const;
|
||||||
@@ -98,14 +91,16 @@ protected:
|
|||||||
[[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
[[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void roomSortOrderChanged();
|
|
||||||
void filterTextChanged();
|
void filterTextChanged();
|
||||||
void activeSpaceIdChanged();
|
void activeSpaceIdChanged();
|
||||||
void modeChanged();
|
void modeChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RoomSortOrder m_sortOrder = Categories;
|
RoomSortOrder m_sortOrder = Activity;
|
||||||
Mode m_mode = All;
|
Mode m_mode = All;
|
||||||
QString m_filterText;
|
QString m_filterText;
|
||||||
QString m_activeSpaceId;
|
QString m_activeSpaceId;
|
||||||
|
|
||||||
|
bool roleCmp(const QVariant &left, const QVariant &right) const;
|
||||||
|
bool prioritiesCmp(const QVector<RoomTreeModel::EventRoles> &priorities, const QModelIndex &left, const QModelIndex &right) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -118,6 +118,10 @@
|
|||||||
<label>Save the collapsed state of the room list</label>
|
<label>Save the collapsed state of the room list</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry name="SortOrder" type="int">
|
||||||
|
<label>The sort order for the rooms in the list.</label>
|
||||||
|
<default>1</default>
|
||||||
|
</entry>
|
||||||
</group>
|
</group>
|
||||||
<group name="NetworkProxy">
|
<group name="NetworkProxy">
|
||||||
<entry name="ProxyType" type="Enum">
|
<entry name="ProxyType" type="Enum">
|
||||||
|
|||||||
@@ -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 {
|
FormCard.FormHeader {
|
||||||
title: i18n("Timeline Events")
|
title: i18n("Timeline Events")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,6 @@ Kirigami.Page {
|
|||||||
property bool filterTextJustChanged: false
|
property bool filterTextJustChanged: false
|
||||||
|
|
||||||
sourceModel: root.roomTreeModel
|
sourceModel: root.roomTreeModel
|
||||||
roomSortOrder: SortFilterRoomTreeModel.Categories
|
|
||||||
activeSpaceId: spaceDrawer.selectedSpaceId
|
activeSpaceId: spaceDrawer.selectedSpaceId
|
||||||
mode: spaceDrawer.showDirectChats ? SortFilterRoomTreeModel.DirectChats : SortFilterRoomTreeModel.Rooms
|
mode: spaceDrawer.showDirectChats ? SortFilterRoomTreeModel.DirectChats : SortFilterRoomTreeModel.Rooms
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user