diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c59a261cb..765b61b9f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -196,6 +196,8 @@ add_library(neochat STATIC messagecomponent.h enums/roomsortparameter.cpp enums/roomsortparameter.h + models/roomsortparametermodel.cpp + models/roomsortparametermodel.h ) set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES diff --git a/src/enums/roomsortparameter.cpp b/src/enums/roomsortparameter.cpp index 62bab0c2a..609c02e23 100644 --- a/src/enums/roomsortparameter.cpp +++ b/src/enums/roomsortparameter.cpp @@ -3,6 +3,11 @@ #include "roomsortparameter.h" +#include + +#include "neochatconfig.h" +#include "neochatroom.h" + namespace { template @@ -16,6 +21,78 @@ int typeCompare(QString left, QString right) { return left.localeAwareCompare(right); } + +static const QList allSortPriorities = { + RoomSortParameter::AlphabeticalAscending, + RoomSortParameter::AlphabeticalDescending, + RoomSortParameter::HasUnread, + RoomSortParameter::MostUnread, + RoomSortParameter::HasHighlight, + RoomSortParameter::MostHighlights, + RoomSortParameter::LastActive, +}; + +static const QList alphabeticalSortPriorities = { + RoomSortParameter::AlphabeticalAscending, +}; + +static const QList activitySortPriorities = { + RoomSortParameter::HasHighlight, + RoomSortParameter::MostHighlights, + RoomSortParameter::HasUnread, + RoomSortParameter::MostUnread, + RoomSortParameter::LastActive, +}; + +static const QList lastMessageSortPriorities = { + RoomSortParameter::LastActive, +}; +} + +QList RoomSortParameter::allParameterList() +{ + return allSortPriorities; +} + +QList RoomSortParameter::currentParameterList() +{ + QList configParamList; + switch (static_cast(NeoChatConfig::sortOrder())) { + case NeoChatConfig::EnumSortOrder::Activity: + configParamList = activitySortPriorities; + break; + case NeoChatConfig::EnumSortOrder::Alphabetical: + configParamList = alphabeticalSortPriorities; + break; + case NeoChatConfig::EnumSortOrder::LastMessage: + configParamList = lastMessageSortPriorities; + break; + case NeoChatConfig::EnumSortOrder::Custom: { + const auto intList = NeoChatConfig::customSortOrder(); + std::transform(intList.constBegin(), intList.constEnd(), std::back_inserter(configParamList), [](int param) { + return static_cast(param); + }); + break; + } + default: + break; + } + + if (configParamList.isEmpty()) { + return activitySortPriorities; + } + return configParamList; +} + +void RoomSortParameter::saveNewParameterList(const QList &newList) +{ + QList intList; + std::transform(newList.constBegin(), newList.constEnd(), std::back_inserter(intList), [](Parameter param) { + return static_cast(param); + }); + NeoChatConfig::setCustomSortOrder(intList); + NeoChatConfig::setSortOrder(NeoChatConfig::EnumSortOrder::Custom); + NeoChatConfig::self()->save(); } int RoomSortParameter::compareParameter(Parameter parameter, NeoChatRoom *leftRoom, NeoChatRoom *rightRoom) @@ -36,7 +113,7 @@ int RoomSortParameter::compareParameter(Parameter parameter, NeoChatRoom *leftRo case LastActive: return compareParameter(leftRoom, rightRoom); default: - return false; + return 0; } } diff --git a/src/enums/roomsortparameter.h b/src/enums/roomsortparameter.h index b44bc9a75..b60ab75fc 100644 --- a/src/enums/roomsortparameter.h +++ b/src/enums/roomsortparameter.h @@ -3,12 +3,13 @@ #pragma once -#include "neochatroom.h" #include #include #include +class NeoChatRoom; + /** * @class RoomSortParameter * @@ -23,15 +24,19 @@ class RoomSortParameter : public QObject public: /** * @brief Defines the available sort parameters. + * + * @note All values are specifically numbered as they should never change even + * if new options are later added. This is because they are stored in + * the config as ints and changing would break someones config on upgrade. */ enum Parameter { - AlphabeticalAscending, - AlphabeticalDescending, - HasUnread, - MostUnread, - HasHighlight, - MostHighlights, - LastActive, + AlphabeticalAscending = 0, + AlphabeticalDescending = 1, + HasUnread = 2, + MostUnread = 3, + HasHighlight = 4, + MostHighlights = 5, + LastActive = 6, }; Q_ENUM(Parameter) @@ -40,7 +45,7 @@ public: * * @sa Parameter */ - static QString parameterName(Parameter parameter) + Q_INVOKABLE static QString parameterName(Parameter parameter) { switch (parameter) { case Parameter::AlphabeticalAscending: @@ -67,7 +72,7 @@ public: * * @sa Parameter */ - static QString parameterDescription(Parameter parameter) + Q_INVOKABLE static QString parameterDescription(Parameter parameter) { switch (parameter) { case Parameter::AlphabeticalAscending: @@ -89,6 +94,21 @@ public: } }; + /** + * @brief List of all available Parameter sort orders. + */ + static QList allParameterList(); + + /** + * @brief The current Parameter sort order list. + */ + static QList currentParameterList(); + + /** + * @brief Save the give Parameter sort order list as the custom sort order. + */ + static void saveNewParameterList(const QList &newList); + /** * @brief Compare the given parameter of the two given rooms. * diff --git a/src/models/roomsortparametermodel.cpp b/src/models/roomsortparametermodel.cpp new file mode 100644 index 000000000..c78ae71c9 --- /dev/null +++ b/src/models/roomsortparametermodel.cpp @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2024 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#include "roomsortparametermodel.h" + +#include "roomsortparameter.h" + +using namespace Qt::StringLiterals; + +RoomSortParameterModel::RoomSortParameterModel(QObject *parent) + : QAbstractListModel(parent) +{ + m_currentParameters = RoomSortParameter::currentParameterList(); +} + +RoomSortParameterModel::RoomSortParameterModel(QList parameters, QObject *parent) + : QAbstractListModel(parent) +{ + m_currentParameters = parameters; +} + +QVariant RoomSortParameterModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= rowCount()) { + return {}; + } + + const auto parameter = m_currentParameters.at(index.row()); + if (role == Name) { + return RoomSortParameter::parameterName(parameter); + } + if (role == Description) { + return RoomSortParameter::parameterDescription(parameter); + } + return {}; +} + +int RoomSortParameterModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_currentParameters.size(); +} + +QHash RoomSortParameterModel::roleNames() const +{ + return { + {Name, "name"}, + {Description, "description"}, + }; +} + +void RoomSortParameterModel::addParameter(RoomSortParameter::Parameter parameter) +{ + if (m_currentParameters.contains(parameter)) { + return; + } + + beginInsertRows({}, rowCount(), rowCount()); + m_currentParameters.append(parameter); + endInsertRows(); +} + +void RoomSortParameterModel::removeRow(int row) +{ + if (rowCount() <= 1 || row < 0 || row >= rowCount()) { + return; + } + + beginRemoveRows({}, row, row); + m_currentParameters.remove(row); + endRemoveRows(); +} + +void RoomSortParameterModel::moveRowUp(int row) +{ + if (row < 1 || row >= rowCount()) { + return; + } + + beginMoveRows({}, row, row, {}, row - 1); + m_currentParameters.move(row, row - 1); + endMoveRows(); +} + +void RoomSortParameterModel::moveRowDown(int row) +{ + if (row < 0 || row >= rowCount() - 1) { + return; + } + + beginMoveRows({}, row, row, {}, row + 2); + m_currentParameters.move(row, row + 1); + endMoveRows(); +} + +void RoomSortParameterModel::saveParameterList() +{ + RoomSortParameter::saveNewParameterList(m_currentParameters); +} + +RoomSortParameterModel *RoomSortParameterModel::allParameterModel() const +{ + return new RoomSortParameterModel(RoomSortParameter::allParameterList()); +} diff --git a/src/models/roomsortparametermodel.h b/src/models/roomsortparametermodel.h new file mode 100644 index 000000000..33a6c9e4f --- /dev/null +++ b/src/models/roomsortparametermodel.h @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: 2024 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#pragma once + +#include +#include + +#include +#include + +#include "enums/roomsortparameter.h" + +/** + * @class RoomSortParameterModel + * + * This model is used to visualize and modify the current sorting priorities. + */ +class RoomSortParameterModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + +public: + /** + * @brief Defines the model roles. + */ + enum Roles { + Name = Qt::DisplayRole, /**< The name of the sort parameter. */ + Description, /**< The description of the sort parameter. */ + }; + Q_ENUM(Roles) + + explicit RoomSortParameterModel(QObject *parent = nullptr); + explicit RoomSortParameterModel(QList parameters, QObject *parent = nullptr); + + /** + * @brief Get the given role value at the given index. + * + * @sa QAbstractItemModel::data + */ + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + /** + * @brief Number of rows in the model. + * + * @sa QAbstractItemModel::rowCount + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + /** + * @brief Returns a mapping from Role enum values to role names. + * + * @sa EventRoles, QAbstractItemModel::roleNames() + */ + QHash roleNames() const override; + + /** + * @brief Add the given parameter to the model. + * + * If the Parameter is already in the model nothing will happen. + */ + Q_INVOKABLE void addParameter(RoomSortParameter::Parameter parameter); + + /** + * @brief Remove the given row from the model. + */ + Q_INVOKABLE void removeRow(int row); + + /** + * @brief Move the given row up one. + */ + Q_INVOKABLE void moveRowUp(int row); + + /** + * @brief Move the given row down one. + */ + Q_INVOKABLE void moveRowDown(int row); + + /** + * @brief Save the current model parameters as a custom sort order. + */ + Q_INVOKABLE void saveParameterList(); + + /** + * @brief Return a RoomSortParameterModel with all available parameters. + */ + Q_INVOKABLE RoomSortParameterModel *allParameterModel() const; + +private: + QList m_currentParameters; +}; diff --git a/src/models/sortfilterroomtreemodel.cpp b/src/models/sortfilterroomtreemodel.cpp index 258d9861f..4fdccda09 100644 --- a/src/models/sortfilterroomtreemodel.cpp +++ b/src/models/sortfilterroomtreemodel.cpp @@ -4,6 +4,7 @@ #include "sortfilterroomtreemodel.h" +#include "enums/roomsortparameter.h" #include "neochatconfig.h" #include "neochatconnection.h" #include "neochatroom.h" @@ -65,10 +66,13 @@ static const QVector lastMessageSortPriorities{ RoomSortParameter::LastActive, }; -bool SortFilterRoomTreeModel::prioritiesCmp(const QVector &priorities, - const QModelIndex &source_left, - const QModelIndex &source_right) const +bool SortFilterRoomTreeModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { + // Don't sort the top level categories. + if (!source_left.parent().isValid() || !source_right.parent().isValid()) { + return false; + } + const auto treeModel = dynamic_cast(sourceModel()); if (treeModel == nullptr) { return false; @@ -80,7 +84,7 @@ bool SortFilterRoomTreeModel::prioritiesCmp(const QVector #include -#include "enums/roomsortparameter.h" -#include "models/roomtreemodel.h" +class RoomTreeModel; /** * @class SortFilterRoomTreeModel @@ -105,6 +104,4 @@ private: Mode m_mode = All; QString m_filterText; QString m_activeSpaceId; - - bool prioritiesCmp(const QVector &priorities, const QModelIndex &left, const QModelIndex &right) const; }; diff --git a/src/neochatconfig.kcfg b/src/neochatconfig.kcfg index 867e4dfcc..b0f45eb45 100644 --- a/src/neochatconfig.kcfg +++ b/src/neochatconfig.kcfg @@ -131,9 +131,27 @@ false - + - 1 + + + + + + + + + + + + + + + Activity + + + + diff --git a/src/settings/CMakeLists.txt b/src/settings/CMakeLists.txt index a7e408d7d..c9acbad9a 100644 --- a/src/settings/CMakeLists.txt +++ b/src/settings/CMakeLists.txt @@ -42,4 +42,5 @@ ecm_add_qml_module(settings GENERATE_PLUGIN_SOURCE ThreePIdCard.qml ImportKeysDialog.qml ExportKeysDialog.qml + RoomSortParameterDialog.qml ) diff --git a/src/settings/NeoChatGeneralPage.qml b/src/settings/NeoChatGeneralPage.qml index ee4adf671..69f51ddf5 100644 --- a/src/settings/NeoChatGeneralPage.qml +++ b/src/settings/NeoChatGeneralPage.qml @@ -98,6 +98,7 @@ FormCard.FormCardPage { enabled: !NeoChatConfig.isSortOrderImmutable onToggled: { NeoChatConfig.sortOrder = 1 + NeoChatConfig.customSortOrder = [] NeoChatConfig.save() } } @@ -107,6 +108,7 @@ FormCard.FormCardPage { enabled: !NeoChatConfig.isSortOrderImmutable onToggled: { NeoChatConfig.sortOrder = 0 + NeoChatConfig.customSortOrder = [] NeoChatConfig.save() } } @@ -117,9 +119,19 @@ FormCard.FormCardPage { enabled: !NeoChatConfig.isSortOrderImmutable onToggled: { NeoChatConfig.sortOrder = 2 + NeoChatConfig.customSortOrder = [] NeoChatConfig.save() } } + FormCard.FormRadioDelegate { + id: openCustomRoomSortButton + text: i18nc("@option:radio", "Custom") + checked: NeoChatConfig.sortOrder === 3 + enabled: !NeoChatConfig.isSortOrderImmutable + onClicked: { + Qt.createComponent('org.kde.neochat.settings', 'RoomSortParameterDialog').createObject(root).open(); + } + } } FormCard.FormHeader { title: i18n("Timeline Events") diff --git a/src/settings/RoomSortParameterDialog.qml b/src/settings/RoomSortParameterDialog.qml new file mode 100644 index 000000000..f7200c9e5 --- /dev/null +++ b/src/settings/RoomSortParameterDialog.qml @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: 2024 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts + +import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.delegates as Delegates +import org.kde.kirigamiaddons.formcard as FormCard + +import org.kde.neochat + +Kirigami.Dialog { + id: root + title: i18nc("@title:dialog", "Custom Room Sort Order") + + width: Math.min(parent.width, Kirigami.Units.gridUnit * 24) + height: Math.min(parent.height, Kirigami.Units.gridUnit * 24) + + standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel + + Component.onCompleted: { + header.background.children[0].visible = true + footer.background.children[0].visible = true + } + + onAccepted: roomSortParameterModel.saveParameterList() + + contentItem: QQC2.ScrollView { + clip: true + + ListView { + id: listView + implicitHeight: contentHeight + currentIndex: -1 + + model: RoomSortParameterModel { + id: roomSortParameterModel + } + + delegate: Delegates.RoundedItemDelegate { + id: parameterDelegate + required property string name + required property string description + required property int index + + width: parent?.width ?? 0 + + contentItem: RowLayout { + ColumnLayout { + Layout.fillWidth: true + + QQC2.Label { + Layout.fillWidth: true + text: parameterDelegate.index == 0 ? i18nc("As in first sort chat rooms by the parameter", "first:") : i18nc("As in then sort chat rooms by the parameter", "then:") + } + Kirigami.Heading { + Layout.fillWidth: true + text: parameterDelegate.name + level: 4 + } + QQC2.Label { + Layout.fillWidth: true + text: parameterDelegate.description + color: Kirigami.Theme.disabledTextColor + font: Kirigami.Theme.smallFont + wrapMode: Text.Wrap + } + } + QQC2.ToolButton { + text: i18nc("@button", "Up") + icon.name: "arrow-up" + display: QQC2.AbstractButton.IconOnly + onClicked: roomSortParameterModel.moveRowUp(parameterDelegate.index) + + QQC2.ToolTip.text: text + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + } + QQC2.ToolButton { + text: i18nc("@button", "Down") + icon.name: "arrow-down" + display: QQC2.AbstractButton.IconOnly + onClicked: roomSortParameterModel.moveRowDown(parameterDelegate.index) + + QQC2.ToolTip.text: text + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + } + QQC2.ToolButton { + text: i18nc("@button", "Remove") + icon.name: "list-remove" + display: QQC2.AbstractButton.IconOnly + onClicked: roomSortParameterModel.removeRow(parameterDelegate.index) + + QQC2.ToolTip.text: text + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + } + } + } + + footer: Delegates.RoundedItemDelegate { + text: i18nc("@action:button", "Add parameter") + icon.name: "list-add" + + onClicked: addParameterDialogComponent.createObject(root).open() + } + } + } + + Component { + id: addParameterDialogComponent + + Kirigami.Dialog { + id: addParameterDialog + title: i18nc("@title:dialog", "Select Parameter to Add") + + width: Math.min(parent.width, Kirigami.Units.gridUnit * 24) + height: Math.min(parent.height, Kirigami.Units.gridUnit * 24) + + standardButtons: Kirigami.Dialog.Cancel + + Component.onCompleted: { + header.background.children[0].visible = true + footer.background.children[0].visible = true + } + + contentItem: QQC2.ScrollView { + clip: true + + ListView { + id: listView + implicitHeight: contentHeight + currentIndex: -1 + + model: roomSortParameterModel.allParameterModel() + + delegate: Delegates.RoundedItemDelegate { + id: parameterDelegate + required property string name + required property string description + required property int index + + width: parent?.width ?? 0 + + text: parameterDelegate.name + + contentItem: Delegates.SubtitleContentItem { + itemDelegate: parameterDelegate + subtitle: parameterDelegate.description + } + + onClicked: { + roomSortParameterModel.addParameter(parameterDelegate.index) + addParameterDialog.close() + } + } + } + } + } + } +}